instar 0.8.27 → 0.9.2
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/cli.js +0 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +202 -71
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +38 -4
- package/dist/commands/setup.js.map +1 -1
- package/dist/core/AgentConnector.d.ts +76 -0
- package/dist/core/AgentConnector.d.ts.map +1 -0
- package/dist/core/AgentConnector.js +323 -0
- package/dist/core/AgentConnector.js.map +1 -0
- package/dist/core/AutoUpdater.d.ts +7 -0
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +31 -3
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +86 -5
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/StateWriteAuthority.d.ts +101 -0
- package/dist/core/StateWriteAuthority.d.ts.map +1 -0
- package/dist/core/StateWriteAuthority.js +167 -0
- package/dist/core/StateWriteAuthority.js.map +1 -0
- package/dist/core/types.d.ts +104 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/TopicMemory.d.ts +167 -0
- package/dist/memory/TopicMemory.d.ts.map +1 -0
- package/dist/memory/TopicMemory.js +494 -0
- package/dist/memory/TopicMemory.js.map +1 -0
- package/dist/memory/TopicSummarizer.d.ts +58 -0
- package/dist/memory/TopicSummarizer.d.ts.map +1 -0
- package/dist/memory/TopicSummarizer.js +140 -0
- package/dist/memory/TopicSummarizer.js.map +1 -0
- package/dist/messaging/TelegramAdapter.d.ts +35 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +136 -2
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +340 -1
- package/dist/server/routes.js.map +1 -1
- package/dist/users/UserManager.d.ts +21 -0
- package/dist/users/UserManager.d.ts.map +1 -1
- package/dist/users/UserManager.js +32 -0
- package/dist/users/UserManager.js.map +1 -1
- package/dist/users/UserOnboarding.d.ts +116 -0
- package/dist/users/UserOnboarding.d.ts.map +1 -0
- package/dist/users/UserOnboarding.js +365 -0
- package/dist/users/UserOnboarding.js.map +1 -0
- package/package.json +2 -1
- package/upgrades/0.8.23.md +106 -0
- package/upgrades/0.9.1.md +91 -0
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2CH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAolBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsyBtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDzE"}
|
package/dist/commands/server.js
CHANGED
|
@@ -34,6 +34,7 @@ import { TunnelManager } from '../tunnel/TunnelManager.js';
|
|
|
34
34
|
import { PostUpdateMigrator } from '../core/PostUpdateMigrator.js';
|
|
35
35
|
import { UpgradeGuideProcessor } from '../core/UpgradeGuideProcessor.js';
|
|
36
36
|
import { EvolutionManager } from '../core/EvolutionManager.js';
|
|
37
|
+
import { TopicMemory } from '../memory/TopicMemory.js';
|
|
37
38
|
import { QuotaTracker } from '../monitoring/QuotaTracker.js';
|
|
38
39
|
import { AccountSwitcher } from '../monitoring/AccountSwitcher.js';
|
|
39
40
|
import { QuotaNotifier } from '../monitoring/QuotaNotifier.js';
|
|
@@ -59,55 +60,81 @@ function isAutostartInstalled(projectName) {
|
|
|
59
60
|
return false;
|
|
60
61
|
}
|
|
61
62
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
63
|
+
* Spawn a session for a topic with full conversational context.
|
|
64
|
+
* Shared by both auto-spawn (new topic) and respawn (dead session) paths.
|
|
65
|
+
*
|
|
66
|
+
* Context loading priority (when TopicMemory is available):
|
|
67
|
+
* 1. Rolling conversation summary (captures full history)
|
|
68
|
+
* 2. Recent messages (last 30 — the immediate context)
|
|
69
|
+
* 3. Search instructions (so agent can query deeper history)
|
|
70
|
+
*
|
|
71
|
+
* Fallback: JSONL-based last 20 messages (when TopicMemory unavailable).
|
|
72
|
+
*
|
|
73
|
+
* Returns the new tmux session name.
|
|
64
74
|
*/
|
|
65
|
-
async function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
historyLines.push(`Your task is to continue THIS conversation, not start something new.`);
|
|
76
|
-
historyLines.push(``);
|
|
77
|
-
for (const m of history) {
|
|
78
|
-
const sender = m.fromUser ? 'User' : 'Agent';
|
|
79
|
-
const ts = m.timestamp ? new Date(m.timestamp).toISOString().slice(11, 19) : '??:??';
|
|
80
|
-
const text = (m.text || '').slice(0, 300);
|
|
81
|
-
historyLines.push(`[${ts}] ${sender}: ${text}`);
|
|
82
|
-
}
|
|
83
|
-
historyLines.push(``);
|
|
84
|
-
historyLines.push(`--- End Thread History ---`);
|
|
75
|
+
async function spawnSessionForTopic(sessionManager, telegram, sessionName, topicId, latestMessage, topicMemory) {
|
|
76
|
+
const msg = latestMessage || 'Session started — send a message to continue.';
|
|
77
|
+
let contextContent = '';
|
|
78
|
+
// Prefer TopicMemory (SQLite-backed, with summaries) over raw JSONL scan
|
|
79
|
+
if (topicMemory) {
|
|
80
|
+
try {
|
|
81
|
+
contextContent = topicMemory.formatContextForSession(topicId, 30);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
console.error(`[telegram→session] TopicMemory context failed, falling back to JSONL:`, err);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
// Fallback to JSONL-based history
|
|
88
|
+
if (!contextContent) {
|
|
89
|
+
try {
|
|
90
|
+
const history = telegram.getTopicHistory(topicId, 20);
|
|
91
|
+
if (history.length > 0) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
lines.push(`--- Thread History (last ${history.length} messages) ---`);
|
|
94
|
+
lines.push(`IMPORTANT: Read this history carefully before taking any action.`);
|
|
95
|
+
lines.push(`Your task is to continue THIS conversation, not start something new.`);
|
|
96
|
+
lines.push(``);
|
|
97
|
+
for (const m of history) {
|
|
98
|
+
const sender = m.fromUser ? 'User' : 'Agent';
|
|
99
|
+
const ts = m.timestamp ? new Date(m.timestamp).toISOString().slice(11, 19) : '??:??';
|
|
100
|
+
const text = (m.text || '').slice(0, 300);
|
|
101
|
+
lines.push(`[${ts}] ${sender}: ${text}`);
|
|
102
|
+
}
|
|
103
|
+
lines.push(``);
|
|
104
|
+
lines.push(`--- End Thread History ---`);
|
|
105
|
+
contextContent = lines.join('\n');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
console.error(`[telegram→session] Failed to fetch thread history:`, err);
|
|
110
|
+
}
|
|
89
111
|
}
|
|
90
|
-
//
|
|
91
|
-
// Thread history and context go into temp files for Claude to read.
|
|
112
|
+
// Write context to temp file for Claude to read
|
|
92
113
|
const tmpDir = '/tmp/instar-telegram';
|
|
93
114
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
94
115
|
let bootstrapMessage;
|
|
95
|
-
|
|
96
|
-
if (historyLines.length > 0) {
|
|
97
|
-
const historyContent = historyLines.join('\n');
|
|
116
|
+
if (contextContent) {
|
|
98
117
|
const filepath = path.join(tmpDir, `history-${topicId}-${Date.now()}-${process.pid}.txt`);
|
|
99
|
-
fs.writeFileSync(filepath,
|
|
100
|
-
|
|
101
|
-
bootstrapMessage = `[telegram:${topicId}] ${msg} (Session respawned. Thread history at ${filepath} — read it for context before responding. ${relayNote})`;
|
|
118
|
+
fs.writeFileSync(filepath, contextContent);
|
|
119
|
+
bootstrapMessage = `[telegram:${topicId}] ${msg} (Thread history and context at ${filepath} — read it for context before responding.)`;
|
|
102
120
|
}
|
|
103
121
|
else {
|
|
104
|
-
bootstrapMessage = `[telegram:${topicId}] ${msg}
|
|
122
|
+
bootstrapMessage = `[telegram:${topicId}] ${msg}`;
|
|
105
123
|
}
|
|
124
|
+
const newSessionName = await sessionManager.spawnInteractiveSession(bootstrapMessage, sessionName, { telegramTopicId: topicId });
|
|
125
|
+
return newSessionName;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Respawn a session for a topic, including thread history in the bootstrap.
|
|
129
|
+
* This prevents "thread drift" where respawned sessions lose context.
|
|
130
|
+
*/
|
|
131
|
+
async function respawnSessionForTopic(sessionManager, telegram, targetSession, topicId, latestMessage, topicMemory) {
|
|
132
|
+
console.log(`[telegram→session] Session "${targetSession}" needs respawn for topic ${topicId}`);
|
|
106
133
|
const storedName = telegram.getTopicName(topicId);
|
|
107
134
|
// Use topic name, not tmux session name — tmux names include the project prefix
|
|
108
135
|
// which causes cascading names like ai-guy-ai-guy-ai-guy-topic-1 on each respawn.
|
|
109
136
|
const topicName = storedName || `topic-${topicId}`;
|
|
110
|
-
const newSessionName = await sessionManager
|
|
137
|
+
const newSessionName = await spawnSessionForTopic(sessionManager, telegram, topicName, topicId, latestMessage, topicMemory);
|
|
111
138
|
telegram.registerTopicSession(topicId, newSessionName);
|
|
112
139
|
await telegram.sendToTopic(topicId, `Session respawned.`);
|
|
113
140
|
console.log(`[telegram→session] Respawned "${newSessionName}" for topic ${topicId}`);
|
|
@@ -116,7 +143,7 @@ async function respawnSessionForTopic(sessionManager, telegram, targetSession, t
|
|
|
116
143
|
* Wire up Telegram session management callbacks.
|
|
117
144
|
* These enable /interrupt, /restart, /sessions commands and stall detection.
|
|
118
145
|
*/
|
|
119
|
-
function wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, accountSwitcher, claudePath) {
|
|
146
|
+
function wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, accountSwitcher, claudePath, topicMemory) {
|
|
120
147
|
// /interrupt — send Escape key to a tmux session
|
|
121
148
|
telegram.onInterruptSession = async (sessionName) => {
|
|
122
149
|
try {
|
|
@@ -137,7 +164,7 @@ function wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, ac
|
|
|
137
164
|
}
|
|
138
165
|
catch { /* may already be dead */ }
|
|
139
166
|
// Respawn with thread history
|
|
140
|
-
await respawnSessionForTopic(sessionManager, telegram, sessionName, topicId);
|
|
167
|
+
await respawnSessionForTopic(sessionManager, telegram, sessionName, topicId, undefined, topicMemory);
|
|
141
168
|
};
|
|
142
169
|
// /sessions — list running sessions
|
|
143
170
|
telegram.onListSessions = () => {
|
|
@@ -332,7 +359,7 @@ function wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, ac
|
|
|
332
359
|
* Wire up Telegram message routing: topic messages → Claude sessions.
|
|
333
360
|
* This is the core handler that makes Telegram topics work like sessions.
|
|
334
361
|
*/
|
|
335
|
-
function wireTelegramRouting(telegram, sessionManager, quotaTracker) {
|
|
362
|
+
function wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory) {
|
|
336
363
|
telegram.onTopicMessage = (msg) => {
|
|
337
364
|
const topicId = msg.metadata?.messageThreadId ?? null;
|
|
338
365
|
if (!topicId)
|
|
@@ -390,37 +417,19 @@ function wireTelegramRouting(telegram, sessionManager, quotaTracker) {
|
|
|
390
417
|
catch { /* classification failed — fall through to respawn */ }
|
|
391
418
|
if (!isQuotaDeath) {
|
|
392
419
|
telegram.sendToTopic(topicId, `🔄 Session restarting — message queued.`).catch(() => { });
|
|
393
|
-
respawnSessionForTopic(sessionManager, telegram, targetSession, topicId, text).catch(err => {
|
|
420
|
+
respawnSessionForTopic(sessionManager, telegram, targetSession, topicId, text, topicMemory).catch(err => {
|
|
394
421
|
console.error(`[telegram→session] Respawn failed:`, err);
|
|
395
422
|
});
|
|
396
423
|
}
|
|
397
424
|
}
|
|
398
425
|
}
|
|
399
426
|
else {
|
|
400
|
-
// No session mapped — auto-spawn
|
|
401
|
-
|
|
427
|
+
// No session mapped — auto-spawn with topic history (same as respawn path).
|
|
428
|
+
// Without history, the agent has no conversational context and gives blind answers.
|
|
429
|
+
console.log(`[telegram→session] No session for topic ${topicId}, auto-spawning with history...`);
|
|
402
430
|
const storedName = telegram.getTopicName(topicId) || `topic-${topicId}`;
|
|
403
|
-
//
|
|
404
|
-
|
|
405
|
-
const contextLines = [
|
|
406
|
-
`This session was auto-created for Telegram topic ${topicId}.`,
|
|
407
|
-
``,
|
|
408
|
-
`CRITICAL: You MUST relay your response back to Telegram after responding.`,
|
|
409
|
-
`Use the relay script:`,
|
|
410
|
-
``,
|
|
411
|
-
`cat <<'EOF' | .claude/scripts/telegram-reply.sh ${topicId}`,
|
|
412
|
-
`Your response text here`,
|
|
413
|
-
`EOF`,
|
|
414
|
-
``,
|
|
415
|
-
`Strip the [telegram:${topicId}] prefix before interpreting the message.`,
|
|
416
|
-
`Only relay conversational text — not tool output or internal reasoning.`,
|
|
417
|
-
];
|
|
418
|
-
const tmpDir = '/tmp/instar-telegram';
|
|
419
|
-
fs.mkdirSync(tmpDir, { recursive: true });
|
|
420
|
-
const ctxPath = path.join(tmpDir, `ctx-${topicId}-${Date.now()}.txt`);
|
|
421
|
-
fs.writeFileSync(ctxPath, contextLines.join('\n'));
|
|
422
|
-
const bootstrapMessage = `[telegram:${topicId}] ${text} (IMPORTANT: Read ${ctxPath} for Telegram relay instructions — you MUST relay your response back.)`;
|
|
423
|
-
sessionManager.spawnInteractiveSession(bootstrapMessage, storedName, { telegramTopicId: topicId }).then((newSessionName) => {
|
|
431
|
+
// Use the shared spawn helper that includes topic history
|
|
432
|
+
spawnSessionForTopic(sessionManager, telegram, storedName, topicId, text, topicMemory).then((newSessionName) => {
|
|
424
433
|
telegram.registerTopicSession(topicId, newSessionName);
|
|
425
434
|
telegram.sendToTopic(topicId, `Session starting up — reading your message now. One moment.`).catch(() => { });
|
|
426
435
|
console.log(`[telegram→session] Auto-spawned "${newSessionName}" for topic ${topicId}`);
|
|
@@ -739,6 +748,7 @@ export async function startServer(options) {
|
|
|
739
748
|
// When --no-telegram is set (lifeline owns polling), create adapter in send-only mode
|
|
740
749
|
// so the server can still relay replies via /telegram/reply/:topicId
|
|
741
750
|
let telegram;
|
|
751
|
+
let topicMemory;
|
|
742
752
|
const telegramConfig = config.messaging.find(m => m.type === 'telegram' && m.enabled);
|
|
743
753
|
const skipTelegram = options.telegram === false; // --no-telegram sets telegram: false
|
|
744
754
|
// Standby machines use send-only Telegram — they don't poll for messages
|
|
@@ -777,9 +787,86 @@ export async function startServer(options) {
|
|
|
777
787
|
}, 10 * 60 * 1000);
|
|
778
788
|
console.log(pc.green(' Quota notifications enabled'));
|
|
779
789
|
}
|
|
790
|
+
// Initialize TopicMemory — SQLite-backed conversational memory.
|
|
791
|
+
// Topic history is the primary context for every session.
|
|
792
|
+
topicMemory = new TopicMemory(config.stateDir);
|
|
793
|
+
try {
|
|
794
|
+
await topicMemory.open();
|
|
795
|
+
// Import existing messages from JSONL (idempotent — only inserts new ones)
|
|
796
|
+
const jsonlPath = path.join(config.stateDir, 'telegram-messages.jsonl');
|
|
797
|
+
if (fs.existsSync(jsonlPath)) {
|
|
798
|
+
const imported = topicMemory.importFromJsonl(jsonlPath);
|
|
799
|
+
if (imported > 0) {
|
|
800
|
+
console.log(pc.green(` TopicMemory: imported ${imported} messages from JSONL`));
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
const tmStats = topicMemory.stats();
|
|
804
|
+
console.log(pc.green(` TopicMemory: ${tmStats.totalMessages} messages, ${tmStats.totalTopics} topics, ${tmStats.topicsWithSummaries} summaries`));
|
|
805
|
+
// Wire dual-write: every message logged to JSONL also goes to SQLite
|
|
806
|
+
const tm = topicMemory; // Capture for closure (TypeScript narrowing)
|
|
807
|
+
telegram.onMessageLogged = (entry) => {
|
|
808
|
+
if (entry.topicId != null && tm) {
|
|
809
|
+
tm.insertMessage({
|
|
810
|
+
messageId: entry.messageId,
|
|
811
|
+
topicId: entry.topicId,
|
|
812
|
+
text: entry.text,
|
|
813
|
+
fromUser: entry.fromUser,
|
|
814
|
+
timestamp: entry.timestamp,
|
|
815
|
+
sessionName: entry.sessionName,
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
catch (err) {
|
|
821
|
+
console.error(` TopicMemory init failed (non-critical): ${err instanceof Error ? err.message : err}`);
|
|
822
|
+
}
|
|
780
823
|
// Wire up topic → session routing and session management callbacks
|
|
781
|
-
wireTelegramRouting(telegram, sessionManager, quotaTracker);
|
|
782
|
-
wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, accountSwitcher, config.sessions.claudePath);
|
|
824
|
+
wireTelegramRouting(telegram, sessionManager, quotaTracker, topicMemory);
|
|
825
|
+
wireTelegramCallbacks(telegram, sessionManager, state, quotaTracker, accountSwitcher, config.sessions.claudePath, topicMemory);
|
|
826
|
+
// Wire up unknown-user handling (Multi-User Setup Wizard Phase 4.5)
|
|
827
|
+
telegram.onGetRegistrationPolicy = () => ({
|
|
828
|
+
policy: config.userRegistrationPolicy ?? 'admin-only',
|
|
829
|
+
contactHint: config.registrationContactHint,
|
|
830
|
+
agentName: config.projectName,
|
|
831
|
+
});
|
|
832
|
+
telegram.onNotifyAdminJoinRequest = async (request) => {
|
|
833
|
+
const { JoinRequestManager } = await import('../users/UserOnboarding.js');
|
|
834
|
+
const joinManager = new JoinRequestManager(config.stateDir);
|
|
835
|
+
const joinRequest = joinManager.createRequest(request.name, request.telegramUserId, null);
|
|
836
|
+
// Notify admin via Lifeline topic (the always-available admin channel)
|
|
837
|
+
const lifelineTopicId = telegram.getLifelineTopicId();
|
|
838
|
+
if (lifelineTopicId) {
|
|
839
|
+
const userLabel = request.username ? `@${request.username}` : request.name;
|
|
840
|
+
await telegram.sendToTopic(lifelineTopicId, `\ud83d\udc64 **Join Request** from ${userLabel} (ID: ${request.telegramUserId})\n\n` +
|
|
841
|
+
`To approve: \`/approve ${joinRequest.approvalCode}\`\n` +
|
|
842
|
+
`To deny: \`/deny ${joinRequest.approvalCode}\``).catch(() => { });
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
telegram.onStartMiniOnboarding = async (telegramUserId, firstName, username) => {
|
|
846
|
+
const { buildUserProfile, buildCondensedConsentDisclosure } = await import('../users/UserOnboarding.js');
|
|
847
|
+
// Send consent disclosure first
|
|
848
|
+
const consentText = buildCondensedConsentDisclosure(config.projectName);
|
|
849
|
+
await telegram.sendToTopic(1, consentText).catch(() => { }); // General topic
|
|
850
|
+
// Build a basic profile (consent will be confirmed via follow-up reply)
|
|
851
|
+
const profile = buildUserProfile({
|
|
852
|
+
name: firstName,
|
|
853
|
+
telegramUserId,
|
|
854
|
+
});
|
|
855
|
+
// Add to users via UserManager
|
|
856
|
+
const { UserManager } = await import('../users/UserManager.js');
|
|
857
|
+
const userManager = new UserManager(config.stateDir, config.users);
|
|
858
|
+
userManager.upsertUser(profile);
|
|
859
|
+
// Add to authorized user IDs so future messages are accepted
|
|
860
|
+
const telegramConfig = config.messaging?.find(m => m.type === 'telegram');
|
|
861
|
+
if (telegramConfig?.config) {
|
|
862
|
+
const authIds = telegramConfig.config.authorizedUserIds ?? [];
|
|
863
|
+
if (!authIds.includes(telegramUserId)) {
|
|
864
|
+
authIds.push(telegramUserId);
|
|
865
|
+
telegramConfig.config.authorizedUserIds = authIds;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
console.log(`[telegram] Mini-onboarding complete for ${firstName} (${telegramUserId})`);
|
|
869
|
+
};
|
|
783
870
|
console.log(pc.green(' Telegram message routing active'));
|
|
784
871
|
if (scheduler) {
|
|
785
872
|
scheduler.setMessenger(telegram);
|
|
@@ -801,6 +888,30 @@ export async function startServer(options) {
|
|
|
801
888
|
scheduler.notifyJobComplete(session.id, session.tmuxSession);
|
|
802
889
|
});
|
|
803
890
|
}
|
|
891
|
+
// Auto-summarize topics on session completion.
|
|
892
|
+
// When a Telegram-linked session ends, check if its topic needs a summary update.
|
|
893
|
+
// Uses Haiku for cost efficiency — summaries don't need deep reasoning.
|
|
894
|
+
if (topicMemory && telegram) {
|
|
895
|
+
const { TopicSummarizer } = await import('../memory/TopicSummarizer.js');
|
|
896
|
+
const { ClaudeCliIntelligenceProvider } = await import('../core/ClaudeCliIntelligenceProvider.js');
|
|
897
|
+
const summaryIntelligence = new ClaudeCliIntelligenceProvider(config.sessions.claudePath);
|
|
898
|
+
const summarizer = new TopicSummarizer(summaryIntelligence, topicMemory);
|
|
899
|
+
sessionManager.on('sessionComplete', (session) => {
|
|
900
|
+
// Find the topic linked to this session
|
|
901
|
+
const sessionTopicId = telegram.getTopicForSession(session.tmuxSession);
|
|
902
|
+
if (!sessionTopicId)
|
|
903
|
+
return;
|
|
904
|
+
// Check if this topic needs a summary update (async, fire-and-forget)
|
|
905
|
+
summarizer.summarize(sessionTopicId).then((result) => {
|
|
906
|
+
if (result) {
|
|
907
|
+
console.log(`[TopicSummarizer] Updated summary for topic ${sessionTopicId}: ${result.messagesProcessed} messages processed in ${result.durationMs}ms`);
|
|
908
|
+
}
|
|
909
|
+
}).catch((err) => {
|
|
910
|
+
console.error(`[TopicSummarizer] Failed for topic ${sessionTopicId}: ${err instanceof Error ? err.message : err}`);
|
|
911
|
+
});
|
|
912
|
+
});
|
|
913
|
+
console.log(pc.green(' Topic auto-summarization enabled (on session end)'));
|
|
914
|
+
}
|
|
804
915
|
// Session Watchdog — auto-remediation for stuck commands
|
|
805
916
|
let watchdog;
|
|
806
917
|
if (config.monitoring.watchdog?.enabled) {
|
|
@@ -981,7 +1092,7 @@ export async function startServer(options) {
|
|
|
981
1092
|
}
|
|
982
1093
|
});
|
|
983
1094
|
sleepWakeDetector.start();
|
|
984
|
-
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, publisher, viewer, tunnel, evolution, watchdog, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem });
|
|
1095
|
+
const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, publisher, viewer, tunnel, evolution, watchdog, topicMemory, coordinator: coordinator.enabled ? coordinator : undefined, localSigningKeyPem });
|
|
985
1096
|
await server.start();
|
|
986
1097
|
// Start tunnel AFTER server is listening
|
|
987
1098
|
if (tunnel) {
|
|
@@ -1045,15 +1156,17 @@ export async function startServer(options) {
|
|
|
1045
1156
|
await sessionManager.spawnSession({
|
|
1046
1157
|
name: 'upgrade-notify',
|
|
1047
1158
|
prompt: [
|
|
1048
|
-
'IMPORTANT: You are a SHORT-LIVED session with
|
|
1159
|
+
'IMPORTANT: You are a SHORT-LIVED session with a SPECIFIC task. Do NOT search for files or explore the codebase. Everything you need is in this prompt.',
|
|
1049
1160
|
'',
|
|
1050
|
-
'You have been updated to a new Instar version. Read the upgrade guide below, then:',
|
|
1161
|
+
'You have been updated to a new Instar version. Read the upgrade guide below, then do ALL THREE steps:',
|
|
1051
1162
|
'',
|
|
1052
|
-
'
|
|
1163
|
+
'## Step 1: Notify your user',
|
|
1164
|
+
'',
|
|
1165
|
+
'Compose a brief, personalized message (3-8 sentences) for your user about the new features.',
|
|
1053
1166
|
' RULES:',
|
|
1054
1167
|
' - Write like you\'re texting a friend — warm, conversational, no jargon',
|
|
1055
1168
|
' - This should NOT look like a changelog or release notes',
|
|
1056
|
-
' - Lead with the biggest USER-VISIBLE feature
|
|
1169
|
+
' - Lead with the biggest USER-VISIBLE feature',
|
|
1057
1170
|
' - Include CONCRETE details — actual URLs, PINs, things they can click/use right now',
|
|
1058
1171
|
' - NEVER mention "bearer tokens", "auth tokens", version numbers in headers, or internal implementation details',
|
|
1059
1172
|
' - Focus on what matters to THEM, not internal plumbing',
|
|
@@ -1064,14 +1177,32 @@ export async function startServer(options) {
|
|
|
1064
1177
|
dashboardPin ? ` - Dashboard PIN: ${dashboardPin}` : ' - No dashboard PIN set',
|
|
1065
1178
|
` - Current version: ${getInstalledVersion()}`,
|
|
1066
1179
|
'',
|
|
1067
|
-
`
|
|
1180
|
+
`Send the message via Telegram:`,
|
|
1068
1181
|
hasReplyScript && notifyTopicId
|
|
1069
1182
|
? ` Run: cat <<'MSGEOF' | bash ${replyScript} ${notifyTopicId}\nYOUR_MESSAGE_HERE\nMSGEOF`
|
|
1070
1183
|
: ` Use the telegram-reply script in .instar/scripts/ to send to the updates topic.`,
|
|
1071
1184
|
'',
|
|
1072
|
-
'
|
|
1185
|
+
'## Step 2: Update your memory with new capabilities',
|
|
1186
|
+
'',
|
|
1187
|
+
'Read the upgrade guide\'s "Summary of New Capabilities" section and add the relevant information to your MEMORY.md file (.instar/MEMORY.md).',
|
|
1188
|
+
'This ensures you KNOW about these capabilities in every future session — not just this one.',
|
|
1189
|
+
'',
|
|
1190
|
+
'Add a section like:',
|
|
1191
|
+
'```',
|
|
1192
|
+
'## Capabilities Added in vX.Y.Z',
|
|
1193
|
+
'- Brief description of each capability',
|
|
1194
|
+
'- How to use it (API endpoints, commands, automatic behaviors)',
|
|
1195
|
+
'- Any behavioral changes you should be aware of',
|
|
1196
|
+
'```',
|
|
1197
|
+
'',
|
|
1198
|
+
'Keep it concise — focus on WHAT you can now do and HOW to do it, not the implementation details.',
|
|
1199
|
+
'If there are existing capability notes in MEMORY.md, update or merge rather than duplicate.',
|
|
1200
|
+
'',
|
|
1201
|
+
'## Step 3: Acknowledge',
|
|
1202
|
+
'',
|
|
1203
|
+
'Run: instar upgrade-ack',
|
|
1073
1204
|
'',
|
|
1074
|
-
'
|
|
1205
|
+
'Do all three steps, then exit. Do not search for files or read config files beyond MEMORY.md.',
|
|
1075
1206
|
'',
|
|
1076
1207
|
'--- UPGRADE GUIDE ---',
|
|
1077
1208
|
guideContent,
|