morpheus-cli 0.9.0 → 0.9.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/dist/channels/discord.js +133 -6
- package/dist/channels/telegram.js +23 -17
- package/dist/http/api.js +2 -3
- package/dist/runtime/apoc.js +1 -1
- package/dist/runtime/keymaker.js +1 -1
- package/dist/runtime/memory/sati/service.js +1 -1
- package/dist/runtime/memory/sqlite.js +31 -124
- package/dist/runtime/neo.js +1 -1
- package/dist/runtime/oracle.js +30 -52
- package/dist/runtime/smiths/delegator.js +1 -1
- package/dist/runtime/trinity.js +1 -1
- package/dist/ui/assets/{AuditDashboard-5sA8Sd8S.js → AuditDashboard-nVV9KKFp.js} +1 -1
- package/dist/ui/assets/Chat-ChsmnZzq.js +41 -0
- package/dist/ui/assets/{Chronos-BAjeLobF.js → Chronos-kgO7IkEj.js} +1 -1
- package/dist/ui/assets/{ConfirmationModal-fvgnOWTY.js → ConfirmationModal-D1BYPXJ4.js} +1 -1
- package/dist/ui/assets/{Dashboard-Ca5mSefz.js → Dashboard-DWB5NwQn.js} +1 -1
- package/dist/ui/assets/{DeleteConfirmationModal-A8EmnHoa.js → DeleteConfirmationModal-CgIMbyB7.js} +1 -1
- package/dist/ui/assets/{Logs-CYu7se7R.js → Logs-DGdRnEFi.js} +1 -1
- package/dist/ui/assets/{MCPManager-DsDA_ZVT.js → MCPManager-BDjWMRRX.js} +1 -1
- package/dist/ui/assets/{ModelPricing-DnSm_Nh-.js → ModelPricing-DAk1sS7D.js} +1 -1
- package/dist/ui/assets/{Notifications-CiljQzvM.js → Notifications-DMEq6EZR.js} +1 -1
- package/dist/ui/assets/{SatiMemories-rnO2b0LG.js → SatiMemories-BxicQE35.js} +1 -1
- package/dist/ui/assets/{SessionAudit-Dfvhge3Z.js → SessionAudit-CKJQf9LU.js} +1 -1
- package/dist/ui/assets/{Settings-OQlHAJoy.js → Settings-CulMd4Qr.js} +1 -1
- package/dist/ui/assets/{Skills-Crsybug0.js → Skills-DPoqYa8Y.js} +1 -1
- package/dist/ui/assets/{Smiths-wm90jRDT.js → Smiths-Clamjlph.js} +1 -1
- package/dist/ui/assets/{Tasks-C5FMu_Yu.js → Tasks-BfTkhB1J.js} +1 -1
- package/dist/ui/assets/{TrinityDatabases-BzYfecKI.js → TrinityDatabases-BmM1S9aQ.js} +1 -1
- package/dist/ui/assets/{UsageStats-CBo2vW2n.js → UsageStats-aAu2DFlb.js} +1 -1
- package/dist/ui/assets/{WebhookManager-0tDFkfHd.js → WebhookManager-DdnRSWl9.js} +1 -1
- package/dist/ui/assets/{audit-B-F8XPLi.js → audit-CqszEkOd.js} +1 -1
- package/dist/ui/assets/{chronos-BvMxfBQH.js → chronos-CPwFWid9.js} +1 -1
- package/dist/ui/assets/{config-DteVgNGR.js → config-D0DePxKu.js} +1 -1
- package/dist/ui/assets/{index-Cwqr-n0Y.js → index-BxVeRyTh.js} +2 -2
- package/dist/ui/assets/index-OLhpm8I7.css +1 -0
- package/dist/ui/assets/{mcp-DxzodOdH.js → mcp-Gjc3IZpO.js} +1 -1
- package/dist/ui/assets/{skills--hAyQnmG.js → skills-B5DnmnHW.js} +1 -1
- package/dist/ui/assets/{stats-Cibaisqd.js → stats-BAse7jj0.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/dist/ui/sw.js +1 -1
- package/package.json +5 -4
- package/dist/ui/assets/Chat-CjxeAQmd.js +0 -41
- package/dist/ui/assets/index-DcfyUdLI.css +0 -1
package/dist/channels/discord.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client, GatewayIntentBits, Partials, Events, ChannelType, REST, Routes, SlashCommandBuilder, } from 'discord.js';
|
|
1
|
+
import { Client, GatewayIntentBits, Partials, Events, ChannelType, REST, Routes, SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType, } from 'discord.js';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
4
|
import path from 'path';
|
|
@@ -26,6 +26,15 @@ const SLASH_COMMANDS = [
|
|
|
26
26
|
.setName('newsession')
|
|
27
27
|
.setDescription('Archive current session and start a new one')
|
|
28
28
|
.setDMPermission(true),
|
|
29
|
+
new SlashCommandBuilder()
|
|
30
|
+
.setName('sessions')
|
|
31
|
+
.setDescription('List all sessions and switch between them')
|
|
32
|
+
.setDMPermission(true),
|
|
33
|
+
new SlashCommandBuilder()
|
|
34
|
+
.setName('session_switch')
|
|
35
|
+
.setDescription('Switch to a specific session')
|
|
36
|
+
.addStringOption(opt => opt.setName('id').setDescription('Session ID to switch to').setRequired(true))
|
|
37
|
+
.setDMPermission(true),
|
|
29
38
|
new SlashCommandBuilder()
|
|
30
39
|
.setName('chronos')
|
|
31
40
|
.setDescription('Schedule a prompt for the Oracle')
|
|
@@ -103,6 +112,8 @@ export class DiscordAdapter {
|
|
|
103
112
|
display = DisplayManager.getInstance();
|
|
104
113
|
config = ConfigManager.getInstance();
|
|
105
114
|
history = new SQLiteChatMessageHistory({ sessionId: '' });
|
|
115
|
+
/** Per-channel session tracking — which session this Discord adapter is currently using */
|
|
116
|
+
currentSessionId = null;
|
|
106
117
|
telephonist = null;
|
|
107
118
|
telephonistProvider = null;
|
|
108
119
|
telephonistModel = null;
|
|
@@ -178,8 +189,8 @@ export class DiscordAdapter {
|
|
|
178
189
|
return;
|
|
179
190
|
this.display.log(`${message.author.tag}: ${text}`, { source: 'Discord' });
|
|
180
191
|
try {
|
|
181
|
-
const sessionId = await this.history.getCurrentSessionOrCreate();
|
|
182
|
-
|
|
192
|
+
const sessionId = this.currentSessionId ?? await this.history.getCurrentSessionOrCreate();
|
|
193
|
+
this.currentSessionId = sessionId;
|
|
183
194
|
const response = await this.oracle.chat(text, undefined, false, {
|
|
184
195
|
origin_channel: 'discord',
|
|
185
196
|
session_id: sessionId,
|
|
@@ -292,8 +303,8 @@ export class DiscordAdapter {
|
|
|
292
303
|
// Show transcription
|
|
293
304
|
await channel.send(`🎤 "${text}"`);
|
|
294
305
|
// Process with Oracle
|
|
295
|
-
const sessionId = await this.history.getCurrentSessionOrCreate();
|
|
296
|
-
|
|
306
|
+
const sessionId = this.currentSessionId ?? await this.history.getCurrentSessionOrCreate();
|
|
307
|
+
this.currentSessionId = sessionId;
|
|
297
308
|
const response = await this.oracle.chat(text, usage, true, {
|
|
298
309
|
origin_channel: 'discord',
|
|
299
310
|
session_id: sessionId,
|
|
@@ -355,6 +366,12 @@ export class DiscordAdapter {
|
|
|
355
366
|
case 'newsession':
|
|
356
367
|
await this.cmdNewSession(interaction);
|
|
357
368
|
break;
|
|
369
|
+
case 'sessions':
|
|
370
|
+
await this.cmdSessions(interaction);
|
|
371
|
+
break;
|
|
372
|
+
case 'session_switch':
|
|
373
|
+
await this.cmdSessionSwitch(interaction);
|
|
374
|
+
break;
|
|
358
375
|
case 'chronos':
|
|
359
376
|
await this.cmdChronos(interaction);
|
|
360
377
|
break;
|
|
@@ -407,6 +424,8 @@ export class DiscordAdapter {
|
|
|
407
424
|
'`/status` — Check Morpheus status',
|
|
408
425
|
'`/stats` — Token usage statistics',
|
|
409
426
|
'`/newsession` — Start a new session',
|
|
427
|
+
'`/sessions` — List all sessions (switch, archive, delete)',
|
|
428
|
+
'`/session_switch id:` — Switch to a specific session',
|
|
410
429
|
'',
|
|
411
430
|
'**Chronos (Scheduler)**',
|
|
412
431
|
'`/chronos prompt: time:` — Schedule a job for the Oracle',
|
|
@@ -474,14 +493,122 @@ export class DiscordAdapter {
|
|
|
474
493
|
async cmdNewSession(interaction) {
|
|
475
494
|
try {
|
|
476
495
|
const history = new SQLiteChatMessageHistory({ sessionId: '' });
|
|
477
|
-
await history.createNewSession();
|
|
496
|
+
const newSessionId = await history.createNewSession();
|
|
478
497
|
history.close();
|
|
498
|
+
// Track the new session as the current one for this Discord channel
|
|
499
|
+
this.currentSessionId = newSessionId;
|
|
479
500
|
await interaction.reply({ content: '✅ New session started.' });
|
|
480
501
|
}
|
|
481
502
|
catch (err) {
|
|
482
503
|
await interaction.reply({ content: `Error: ${err.message}` });
|
|
483
504
|
}
|
|
484
505
|
}
|
|
506
|
+
async cmdSessions(interaction) {
|
|
507
|
+
try {
|
|
508
|
+
const history = new SQLiteChatMessageHistory({ sessionId: '' });
|
|
509
|
+
const sessions = (await history.listSessions()).filter((s) => !s.id.startsWith('chronos-job-') && !s.id.startsWith('sati-evaluation'));
|
|
510
|
+
history.close();
|
|
511
|
+
if (sessions.length === 0) {
|
|
512
|
+
await interaction.reply({ content: 'No sessions found.' });
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const lines = ['**Sessions:**\n'];
|
|
516
|
+
const rows = [];
|
|
517
|
+
for (const session of sessions) {
|
|
518
|
+
const title = session.title || 'Untitled Session';
|
|
519
|
+
const isCurrent = session.id === this.currentSessionId;
|
|
520
|
+
const icon = isCurrent ? '🟢' : '⚪';
|
|
521
|
+
const started = new Date(session.started_at).toLocaleString();
|
|
522
|
+
lines.push(`${icon} **${title}**`);
|
|
523
|
+
lines.push(` ID: \`${session.id}\``);
|
|
524
|
+
lines.push(` Started: ${started}\n`);
|
|
525
|
+
// Discord allows max 5 buttons per row, max 5 rows per message
|
|
526
|
+
if (rows.length < 5) {
|
|
527
|
+
const btns = [];
|
|
528
|
+
if (!isCurrent) {
|
|
529
|
+
btns.push(new ButtonBuilder()
|
|
530
|
+
.setCustomId(`session_switch_${session.id}`)
|
|
531
|
+
.setLabel('Switch')
|
|
532
|
+
.setStyle(ButtonStyle.Primary)
|
|
533
|
+
.setEmoji('➡️'));
|
|
534
|
+
}
|
|
535
|
+
btns.push(new ButtonBuilder()
|
|
536
|
+
.setCustomId(`session_archive_${session.id}`)
|
|
537
|
+
.setLabel('Archive')
|
|
538
|
+
.setStyle(ButtonStyle.Secondary)
|
|
539
|
+
.setEmoji('📂'));
|
|
540
|
+
btns.push(new ButtonBuilder()
|
|
541
|
+
.setCustomId(`session_delete_${session.id}`)
|
|
542
|
+
.setLabel('Delete')
|
|
543
|
+
.setStyle(ButtonStyle.Danger)
|
|
544
|
+
.setEmoji('🗑️'));
|
|
545
|
+
rows.push(new ActionRowBuilder().addComponents(...btns));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
const content = lines.join('\n').slice(0, 2000);
|
|
549
|
+
const reply = await interaction.reply({ content, components: rows, fetchReply: true });
|
|
550
|
+
// Collect button interactions for 60 seconds
|
|
551
|
+
const collector = reply.createMessageComponentCollector({
|
|
552
|
+
componentType: ComponentType.Button,
|
|
553
|
+
time: 60_000,
|
|
554
|
+
});
|
|
555
|
+
collector.on('collect', async (btn) => {
|
|
556
|
+
try {
|
|
557
|
+
if (btn.customId.startsWith('session_switch_')) {
|
|
558
|
+
const sid = btn.customId.replace('session_switch_', '');
|
|
559
|
+
const h = new SQLiteChatMessageHistory({ sessionId: '' });
|
|
560
|
+
await h.switchSession(sid);
|
|
561
|
+
h.close();
|
|
562
|
+
this.currentSessionId = sid;
|
|
563
|
+
await btn.reply({ content: `✅ Switched to session \`${sid}\`.`, ephemeral: true });
|
|
564
|
+
}
|
|
565
|
+
else if (btn.customId.startsWith('session_archive_')) {
|
|
566
|
+
const sid = btn.customId.replace('session_archive_', '');
|
|
567
|
+
const h = new SQLiteChatMessageHistory({ sessionId: '' });
|
|
568
|
+
await h.archiveSession(sid);
|
|
569
|
+
h.close();
|
|
570
|
+
if (this.currentSessionId === sid)
|
|
571
|
+
this.currentSessionId = null;
|
|
572
|
+
await btn.reply({ content: `📂 Session \`${sid}\` archived.`, ephemeral: true });
|
|
573
|
+
}
|
|
574
|
+
else if (btn.customId.startsWith('session_delete_')) {
|
|
575
|
+
const sid = btn.customId.replace('session_delete_', '');
|
|
576
|
+
const h = new SQLiteChatMessageHistory({ sessionId: '' });
|
|
577
|
+
await h.deleteSession(sid);
|
|
578
|
+
h.close();
|
|
579
|
+
if (this.currentSessionId === sid)
|
|
580
|
+
this.currentSessionId = null;
|
|
581
|
+
await btn.reply({ content: `🗑️ Session \`${sid}\` deleted.`, ephemeral: true });
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
catch (err) {
|
|
585
|
+
await btn.reply({ content: `Error: ${err.message}`, ephemeral: true }).catch(() => { });
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
collector.on('end', async () => {
|
|
589
|
+
try {
|
|
590
|
+
await interaction.editReply({ components: [] });
|
|
591
|
+
}
|
|
592
|
+
catch { /* message may have been deleted */ }
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
catch (err) {
|
|
596
|
+
await interaction.reply({ content: `Error: ${err.message}` });
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
async cmdSessionSwitch(interaction) {
|
|
600
|
+
const sessionId = interaction.options.getString('id', true);
|
|
601
|
+
try {
|
|
602
|
+
const history = new SQLiteChatMessageHistory({ sessionId: '' });
|
|
603
|
+
await history.switchSession(sessionId);
|
|
604
|
+
history.close();
|
|
605
|
+
this.currentSessionId = sessionId;
|
|
606
|
+
await interaction.reply({ content: `✅ Switched to session \`${sessionId}\`.` });
|
|
607
|
+
}
|
|
608
|
+
catch (err) {
|
|
609
|
+
await interaction.reply({ content: `Error: ${err.message}` });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
485
612
|
async cmdChronos(interaction) {
|
|
486
613
|
const prompt = interaction.options.getString('prompt', true);
|
|
487
614
|
const timeExpr = interaction.options.getString('time', true);
|
|
@@ -144,6 +144,8 @@ export class TelegramAdapter {
|
|
|
144
144
|
telephonistProvider = null;
|
|
145
145
|
telephonistModel = null;
|
|
146
146
|
history = new SQLiteChatMessageHistory({ sessionId: '' });
|
|
147
|
+
/** Per-channel session tracking — which session this Telegram adapter is currently using */
|
|
148
|
+
currentSessionId = null;
|
|
147
149
|
RATE_LIMIT_MS = 3000; // minimum ms between requests per user
|
|
148
150
|
rateLimiter = new Map(); // userId -> last request timestamp
|
|
149
151
|
// Pending Chronos create confirmations (userId -> job data + expiry)
|
|
@@ -230,8 +232,8 @@ export class TelegramAdapter {
|
|
|
230
232
|
try {
|
|
231
233
|
// Send "typing" status
|
|
232
234
|
await ctx.sendChatAction('typing');
|
|
233
|
-
const sessionId = await this.history.getCurrentSessionOrCreate();
|
|
234
|
-
|
|
235
|
+
const sessionId = this.currentSessionId ?? await this.history.getCurrentSessionOrCreate();
|
|
236
|
+
this.currentSessionId = sessionId;
|
|
235
237
|
// Process with Agent
|
|
236
238
|
const response = await this.oracle.chat(text, undefined, false, {
|
|
237
239
|
origin_channel: 'telegram',
|
|
@@ -317,9 +319,9 @@ export class TelegramAdapter {
|
|
|
317
319
|
this.display.log(`Transcription success for @${user}: "${text}"`, { source: 'Telephonist', level: 'success' });
|
|
318
320
|
// Audit: record telephonist execution
|
|
319
321
|
try {
|
|
320
|
-
const
|
|
322
|
+
const auditSessionId = this.currentSessionId ?? await this.history.getCurrentSessionOrCreate();
|
|
321
323
|
AuditRepository.getInstance().insert({
|
|
322
|
-
session_id:
|
|
324
|
+
session_id: auditSessionId,
|
|
323
325
|
event_type: 'telephonist',
|
|
324
326
|
agent: 'telephonist',
|
|
325
327
|
provider: config.audio.provider,
|
|
@@ -345,8 +347,8 @@ export class TelegramAdapter {
|
|
|
345
347
|
// So I should treat 'text' as if it was a text message.
|
|
346
348
|
await ctx.reply(`🎤 Transcription: "${text}"`);
|
|
347
349
|
await ctx.sendChatAction('typing');
|
|
348
|
-
const sessionId = await this.history.getCurrentSessionOrCreate();
|
|
349
|
-
|
|
350
|
+
const sessionId = this.currentSessionId ?? await this.history.getCurrentSessionOrCreate();
|
|
351
|
+
this.currentSessionId = sessionId;
|
|
350
352
|
// Process with Agent
|
|
351
353
|
const response = await this.oracle.chat(text, usage, true, {
|
|
352
354
|
origin_channel: 'telegram',
|
|
@@ -380,9 +382,9 @@ export class TelegramAdapter {
|
|
|
380
382
|
const detail = error?.cause?.message || error?.response?.data?.error?.message || error.message;
|
|
381
383
|
this.display.log(`Audio processing error for @${user}: ${detail}`, { source: 'Telephonist', level: 'error' });
|
|
382
384
|
try {
|
|
383
|
-
const
|
|
385
|
+
const auditSessionId = this.currentSessionId ?? 'default';
|
|
384
386
|
AuditRepository.getInstance().insert({
|
|
385
|
-
session_id:
|
|
387
|
+
session_id: auditSessionId,
|
|
386
388
|
event_type: 'telephonist',
|
|
387
389
|
agent: 'telephonist',
|
|
388
390
|
provider: this.config.get().audio.provider,
|
|
@@ -428,12 +430,14 @@ export class TelegramAdapter {
|
|
|
428
430
|
return;
|
|
429
431
|
}
|
|
430
432
|
try {
|
|
431
|
-
//
|
|
433
|
+
// Validate session exists and is usable
|
|
432
434
|
const history = new SQLiteChatMessageHistory({ sessionId: "" });
|
|
433
|
-
// Alternar para a nova sessão
|
|
434
435
|
await history.switchSession(sessionId);
|
|
436
|
+
history.close();
|
|
437
|
+
// Track this session as the current one for this Telegram channel
|
|
438
|
+
this.currentSessionId = sessionId;
|
|
435
439
|
await ctx.answerCbQuery();
|
|
436
|
-
//
|
|
440
|
+
// Remove the previous message and send confirmation
|
|
437
441
|
if (ctx.updateType === 'callback_query') {
|
|
438
442
|
ctx.deleteMessage().catch(() => { });
|
|
439
443
|
}
|
|
@@ -1154,7 +1158,10 @@ export class TelegramAdapter {
|
|
|
1154
1158
|
async handleApproveNewSessionCommand(ctx, user) {
|
|
1155
1159
|
try {
|
|
1156
1160
|
const history = new SQLiteChatMessageHistory({ sessionId: "" });
|
|
1157
|
-
await history.createNewSession();
|
|
1161
|
+
const newSessionId = await history.createNewSession();
|
|
1162
|
+
history.close();
|
|
1163
|
+
// Track the new session as the current one for this Telegram channel
|
|
1164
|
+
this.currentSessionId = newSessionId;
|
|
1158
1165
|
}
|
|
1159
1166
|
catch (e) {
|
|
1160
1167
|
await ctx.reply(`Error creating new session: ${e.message}`);
|
|
@@ -1167,21 +1174,20 @@ export class TelegramAdapter {
|
|
|
1167
1174
|
// callback_data limit and they are not user-managed sessions.
|
|
1168
1175
|
const sessions = (await history.listSessions()).filter((s) => !s.id.startsWith('chronos-job-') && !s.id.startsWith('sati-evaluation'));
|
|
1169
1176
|
if (sessions.length === 0) {
|
|
1170
|
-
await ctx.reply('No
|
|
1177
|
+
await ctx.reply('No sessions found\\.', { parse_mode: 'MarkdownV2' });
|
|
1171
1178
|
return;
|
|
1172
1179
|
}
|
|
1173
1180
|
let response = '*Sessions:*\n\n';
|
|
1174
1181
|
const keyboard = [];
|
|
1175
1182
|
for (const session of sessions) {
|
|
1176
1183
|
const title = session.title || 'Untitled Session';
|
|
1177
|
-
const
|
|
1184
|
+
const isCurrent = session.id === this.currentSessionId;
|
|
1185
|
+
const statusEmoji = isCurrent ? '🟢' : '⚪';
|
|
1178
1186
|
response += `${statusEmoji} *${escMdRaw(title)}*\n`;
|
|
1179
1187
|
response += `\\- ID: \`${escMdRaw(session.id)}\`\n`;
|
|
1180
|
-
response += `\\- Status: ${escMdRaw(session.status)}\n`;
|
|
1181
1188
|
response += `\\- Started: ${escMdRaw(new Date(session.started_at).toLocaleString())}\n\n`;
|
|
1182
|
-
// Adicionar botão inline para alternar para esta sessão
|
|
1183
1189
|
const sessionButtons = [];
|
|
1184
|
-
if (
|
|
1190
|
+
if (!isCurrent) {
|
|
1185
1191
|
sessionButtons.push({
|
|
1186
1192
|
text: `➡️ Switch`,
|
|
1187
1193
|
callback_data: `switch_session_${session.id}`
|
package/dist/http/api.js
CHANGED
|
@@ -61,8 +61,7 @@ export function createApiRouter(oracle, chronosWorker) {
|
|
|
61
61
|
});
|
|
62
62
|
router.post('/sessions', async (req, res) => {
|
|
63
63
|
try {
|
|
64
|
-
await history.createNewSession();
|
|
65
|
-
const newSessionId = await history.getCurrentSessionOrCreate(); // Should be the new one
|
|
64
|
+
const newSessionId = await history.createNewSession();
|
|
66
65
|
res.json({ success: true, id: newSessionId, message: 'New session started' });
|
|
67
66
|
}
|
|
68
67
|
catch (err) {
|
|
@@ -248,7 +247,7 @@ export function createApiRouter(oracle, chronosWorker) {
|
|
|
248
247
|
}
|
|
249
248
|
try {
|
|
250
249
|
const { message, sessionId } = parsed.data;
|
|
251
|
-
|
|
250
|
+
// Session is passed via taskContext — no need to mutate global Oracle state.
|
|
252
251
|
const response = await oracle.chat(message, undefined, false, {
|
|
253
252
|
origin_channel: 'ui',
|
|
254
253
|
session_id: sessionId,
|
package/dist/runtime/apoc.js
CHANGED
|
@@ -259,7 +259,7 @@ ${context ? `CONTEXT FROM ORACLE:\n${context}` : ""}
|
|
|
259
259
|
try {
|
|
260
260
|
const inputCount = messages.length;
|
|
261
261
|
const startMs = Date.now();
|
|
262
|
-
const response = await this.agent.invoke({ messages }, { recursionLimit:
|
|
262
|
+
const response = await this.agent.invoke({ messages }, { recursionLimit: 50 });
|
|
263
263
|
const durationMs = Date.now() - startMs;
|
|
264
264
|
const apocConfig = this.config.apoc || this.config.llm;
|
|
265
265
|
const lastMessage = response.messages[response.messages.length - 1];
|
package/dist/runtime/keymaker.js
CHANGED
|
@@ -117,7 +117,7 @@ CRITICAL — NEVER FABRICATE DATA:
|
|
|
117
117
|
origin_user_id: taskContext?.origin_user_id,
|
|
118
118
|
};
|
|
119
119
|
const startMs = Date.now();
|
|
120
|
-
const response = await TaskRequestContext.run(invokeContext, () => this.agent.invoke({ messages }, { recursionLimit:
|
|
120
|
+
const response = await TaskRequestContext.run(invokeContext, () => this.agent.invoke({ messages }, { recursionLimit: 50 }));
|
|
121
121
|
const durationMs = Date.now() - startMs;
|
|
122
122
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
123
123
|
const content = typeof lastMessage.content === "string"
|
|
@@ -97,7 +97,7 @@ export class SatiService {
|
|
|
97
97
|
console.warn('[SatiService] Failed to persist input log:', e);
|
|
98
98
|
}
|
|
99
99
|
const satiStartMs = Date.now();
|
|
100
|
-
const response = await agent.invoke({ messages }, { recursionLimit:
|
|
100
|
+
const response = await agent.invoke({ messages }, { recursionLimit: 50 });
|
|
101
101
|
const satiDurationMs = Date.now() - satiStartMs;
|
|
102
102
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
103
103
|
let content = lastMessage.content.toString();
|
|
@@ -716,36 +716,19 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
|
|
|
716
716
|
}
|
|
717
717
|
async createNewSession() {
|
|
718
718
|
const now = Date.now();
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
`).run(activeSession.id);
|
|
733
|
-
}
|
|
734
|
-
// Criar uma nova sessão ativa
|
|
735
|
-
const newId = randomUUID();
|
|
736
|
-
this.db.prepare(`
|
|
737
|
-
INSERT INTO sessions (
|
|
738
|
-
id,
|
|
739
|
-
started_at,
|
|
740
|
-
status
|
|
741
|
-
) VALUES (?, ?, 'active')
|
|
742
|
-
`).run(newId, now);
|
|
743
|
-
// Atualizar o ID da sessão atual desta instância
|
|
744
|
-
this.sessionId = newId;
|
|
745
|
-
this.titleSet = false; // reset cache for new session
|
|
746
|
-
});
|
|
747
|
-
tx(); // Executar a transação
|
|
748
|
-
this.display.log('✅ Nova sessão iniciada e sessão anterior pausada', { source: 'Sati' });
|
|
719
|
+
const newId = randomUUID();
|
|
720
|
+
this.db.prepare(`
|
|
721
|
+
INSERT INTO sessions (
|
|
722
|
+
id,
|
|
723
|
+
started_at,
|
|
724
|
+
status
|
|
725
|
+
) VALUES (?, ?, 'active')
|
|
726
|
+
`).run(newId, now);
|
|
727
|
+
// Update this instance to point to the new session
|
|
728
|
+
this.sessionId = newId;
|
|
729
|
+
this.titleSet = false;
|
|
730
|
+
this.display.log('✅ New session created', { source: 'Sati' });
|
|
731
|
+
return newId;
|
|
749
732
|
}
|
|
750
733
|
chunkText(text, chunkSize = 500, overlap = 50) {
|
|
751
734
|
if (!text || text.length === 0)
|
|
@@ -890,27 +873,6 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
|
|
|
890
873
|
`).run(now, sessionId);
|
|
891
874
|
});
|
|
892
875
|
tx(); // Executar a transação
|
|
893
|
-
// Se a sessão era active, verificar se há outra para ativar
|
|
894
|
-
if (session.status === 'active') {
|
|
895
|
-
const nextSession = this.db.prepare(`
|
|
896
|
-
SELECT id FROM sessions
|
|
897
|
-
WHERE status = 'paused'
|
|
898
|
-
ORDER BY started_at DESC
|
|
899
|
-
LIMIT 1
|
|
900
|
-
`).get();
|
|
901
|
-
if (nextSession) {
|
|
902
|
-
// Promover a próxima sessão a ativa
|
|
903
|
-
this.db.prepare(`
|
|
904
|
-
UPDATE sessions
|
|
905
|
-
SET status = 'active'
|
|
906
|
-
WHERE id = ?
|
|
907
|
-
`).run(nextSession.id);
|
|
908
|
-
}
|
|
909
|
-
else {
|
|
910
|
-
// Nenhuma outra sessão, criar nova
|
|
911
|
-
this.createFreshSession();
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
876
|
}
|
|
915
877
|
/**
|
|
916
878
|
* Renomear uma sessão ativa ou pausada.
|
|
@@ -941,101 +903,46 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
|
|
|
941
903
|
tx(); // Executar a transação
|
|
942
904
|
}
|
|
943
905
|
/**
|
|
944
|
-
*
|
|
945
|
-
* Validar sessão alvo: existe e status ∈ (paused, active).
|
|
946
|
-
* Se já for active, não faz nada.
|
|
947
|
-
* Transação: sessão atual active → paused, sessão alvo → active.
|
|
948
|
-
*/
|
|
949
|
-
/**
|
|
950
|
-
* Creates a session row with status 'paused' if it doesn't already exist.
|
|
906
|
+
* Creates a session row with status 'active' if it doesn't already exist.
|
|
951
907
|
* Safe to call multiple times — idempotent.
|
|
952
908
|
*/
|
|
953
909
|
ensureSession(sessionId) {
|
|
954
910
|
const existing = this.db.prepare('SELECT id FROM sessions WHERE id = ?').get(sessionId);
|
|
955
911
|
if (!existing) {
|
|
956
|
-
this.db.prepare("INSERT INTO sessions (id, started_at, status) VALUES (?, ?, '
|
|
912
|
+
this.db.prepare("INSERT INTO sessions (id, started_at, status) VALUES (?, ?, 'active')").run(sessionId, Date.now());
|
|
957
913
|
}
|
|
958
914
|
}
|
|
915
|
+
/**
|
|
916
|
+
* Validates that the target session exists and is usable (not archived/deleted).
|
|
917
|
+
* No longer swaps active↔paused — sessions are independently usable from any channel.
|
|
918
|
+
*/
|
|
959
919
|
async switchSession(targetSessionId) {
|
|
960
|
-
// Validar sessão alvo: existe e status ∈ (paused, active)
|
|
961
920
|
const targetSession = this.db.prepare(`
|
|
962
921
|
SELECT id, status FROM sessions
|
|
963
922
|
WHERE id = ?
|
|
964
923
|
`).get(targetSessionId);
|
|
965
924
|
if (!targetSession) {
|
|
966
|
-
throw new Error(`
|
|
925
|
+
throw new Error(`Session with ID ${targetSessionId} not found.`);
|
|
967
926
|
}
|
|
968
|
-
if (targetSession.status
|
|
969
|
-
throw new Error(`
|
|
927
|
+
if (targetSession.status === 'archived' || targetSession.status === 'deleted') {
|
|
928
|
+
throw new Error(`Session ${targetSessionId} is ${targetSession.status} and cannot be used.`);
|
|
970
929
|
}
|
|
971
|
-
// Se já for active, não faz nada
|
|
972
|
-
if (targetSession.status === 'active') {
|
|
973
|
-
return; // A sessão alvo já está ativa, não precisa fazer nada
|
|
974
|
-
}
|
|
975
|
-
// Transação: sessão atual active → paused, sessão alvo → active
|
|
976
|
-
const tx = this.db.transaction(() => {
|
|
977
|
-
// Pegar a sessão atualmente ativa
|
|
978
|
-
const currentActiveSession = this.db.prepare(`
|
|
979
|
-
SELECT id FROM sessions
|
|
980
|
-
WHERE status = 'active'
|
|
981
|
-
`).get();
|
|
982
|
-
// Se houver uma sessão ativa, mudar seu status para 'paused'
|
|
983
|
-
if (currentActiveSession) {
|
|
984
|
-
this.db.prepare(`
|
|
985
|
-
UPDATE sessions
|
|
986
|
-
SET status = 'paused'
|
|
987
|
-
WHERE id = ?
|
|
988
|
-
`).run(currentActiveSession.id);
|
|
989
|
-
}
|
|
990
|
-
// Mudar o status da sessão alvo para 'active'
|
|
991
|
-
this.db.prepare(`
|
|
992
|
-
UPDATE sessions
|
|
993
|
-
SET status = 'active'
|
|
994
|
-
WHERE id = ?
|
|
995
|
-
`).run(targetSessionId);
|
|
996
|
-
});
|
|
997
|
-
tx(); // Executar a transação
|
|
998
930
|
}
|
|
999
931
|
/**
|
|
1000
|
-
*
|
|
1001
|
-
*
|
|
1002
|
-
* ou criar nova sessão (createFreshSession) e retornar o novo id.
|
|
932
|
+
* Returns the most recently created usable session, or creates one if none exist.
|
|
933
|
+
* A session is usable if its status is 'active' or 'paused' (both are equivalent post-refactor).
|
|
1003
934
|
*/
|
|
1004
935
|
async getCurrentSessionOrCreate() {
|
|
1005
|
-
|
|
1006
|
-
const activeSession = this.db.prepare(`
|
|
1007
|
-
SELECT id FROM sessions
|
|
1008
|
-
WHERE status = 'active'
|
|
1009
|
-
`).get();
|
|
1010
|
-
if (activeSession) {
|
|
1011
|
-
// Se existir, retornar seu id
|
|
1012
|
-
return activeSession.id;
|
|
1013
|
-
}
|
|
1014
|
-
else {
|
|
1015
|
-
// Se não existir, criar nova sessão (createFreshSession) e retornar o novo id
|
|
1016
|
-
const newId = await this.createFreshSession();
|
|
1017
|
-
return newId;
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
async createFreshSession() {
|
|
1021
|
-
// Validar que não existe sessão 'active'
|
|
1022
|
-
const activeSession = this.db.prepare(`
|
|
936
|
+
const session = this.db.prepare(`
|
|
1023
937
|
SELECT id FROM sessions
|
|
1024
|
-
WHERE status
|
|
938
|
+
WHERE status IN ('active', 'paused')
|
|
939
|
+
ORDER BY started_at DESC
|
|
940
|
+
LIMIT 1
|
|
1025
941
|
`).get();
|
|
1026
|
-
if (
|
|
1027
|
-
|
|
942
|
+
if (session) {
|
|
943
|
+
return session.id;
|
|
1028
944
|
}
|
|
1029
|
-
|
|
1030
|
-
const newId = randomUUID();
|
|
1031
|
-
this.db.prepare(`
|
|
1032
|
-
INSERT INTO sessions (
|
|
1033
|
-
id,
|
|
1034
|
-
started_at,
|
|
1035
|
-
status
|
|
1036
|
-
) VALUES (?, ?, 'active')
|
|
1037
|
-
`).run(newId, now);
|
|
1038
|
-
return newId;
|
|
945
|
+
return this.createNewSession();
|
|
1039
946
|
}
|
|
1040
947
|
/**
|
|
1041
948
|
* Lists all active and paused sessions with their basic information.
|
package/dist/runtime/neo.js
CHANGED
|
@@ -137,7 +137,7 @@ ${context ? `Context:\n${context}` : ""}
|
|
|
137
137
|
};
|
|
138
138
|
const inputCount = messages.length;
|
|
139
139
|
const startMs = Date.now();
|
|
140
|
-
const response = await TaskRequestContext.run(invokeContext, () => this.agent.invoke({ messages }, { recursionLimit:
|
|
140
|
+
const response = await TaskRequestContext.run(invokeContext, () => this.agent.invoke({ messages }, { recursionLimit: 50 }));
|
|
141
141
|
const durationMs = Date.now() - startMs;
|
|
142
142
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
143
143
|
const content = typeof lastMessage.content === "string"
|