sensorium-mcp 2.15.3 → 2.16.0

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 (37) hide show
  1. package/dist/http-server.d.ts +16 -0
  2. package/dist/http-server.d.ts.map +1 -0
  3. package/dist/http-server.js +248 -0
  4. package/dist/http-server.js.map +1 -0
  5. package/dist/index.js +133 -1988
  6. package/dist/index.js.map +1 -1
  7. package/dist/response-builders.d.ts +34 -0
  8. package/dist/response-builders.d.ts.map +1 -0
  9. package/dist/response-builders.js +114 -0
  10. package/dist/response-builders.js.map +1 -0
  11. package/dist/stdio-server.d.ts +8 -0
  12. package/dist/stdio-server.d.ts.map +1 -0
  13. package/dist/stdio-server.js +23 -0
  14. package/dist/stdio-server.js.map +1 -0
  15. package/dist/tools/memory-tools.d.ts +36 -0
  16. package/dist/tools/memory-tools.d.ts.map +1 -0
  17. package/dist/tools/memory-tools.js +352 -0
  18. package/dist/tools/memory-tools.js.map +1 -0
  19. package/dist/tools/session-tools.d.ts +46 -0
  20. package/dist/tools/session-tools.d.ts.map +1 -0
  21. package/dist/tools/session-tools.js +255 -0
  22. package/dist/tools/session-tools.js.map +1 -0
  23. package/dist/tools/start-session-tool.d.ts +43 -0
  24. package/dist/tools/start-session-tool.d.ts.map +1 -0
  25. package/dist/tools/start-session-tool.js +188 -0
  26. package/dist/tools/start-session-tool.js.map +1 -0
  27. package/dist/tools/utility-tools.d.ts +34 -0
  28. package/dist/tools/utility-tools.d.ts.map +1 -0
  29. package/dist/tools/utility-tools.js +256 -0
  30. package/dist/tools/utility-tools.js.map +1 -0
  31. package/dist/tools/wait-tool.d.ts +69 -0
  32. package/dist/tools/wait-tool.d.ts.map +1 -0
  33. package/dist/tools/wait-tool.js +702 -0
  34. package/dist/tools/wait-tool.js.map +1 -0
  35. package/dist/types.d.ts +2 -0
  36. package/dist/types.d.ts.map +1 -1
  37. package/package.json +1 -1
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Session tool handlers extracted from index.ts.
3
+ *
4
+ * Handles: report_progress, hibernate
5
+ */
6
+ import { convertMarkdown, splitMessage } from "../markdown.js";
7
+ import { errorMessage } from "../utils.js";
8
+ // ---------------------------------------------------------------------------
9
+ // Dispatcher
10
+ // ---------------------------------------------------------------------------
11
+ export async function handleSessionTool(name, args, ctx, extra) {
12
+ switch (name) {
13
+ case "report_progress":
14
+ return handleReportProgress(args, ctx);
15
+ case "hibernate":
16
+ return handleHibernate(args, ctx, extra);
17
+ default:
18
+ return ctx.errorResult(`Unknown session tool: ${name}`);
19
+ }
20
+ }
21
+ // ---------------------------------------------------------------------------
22
+ // report_progress
23
+ // ---------------------------------------------------------------------------
24
+ async function handleReportProgress(args, ctx) {
25
+ const { resolveThreadId, getShortReminder, errorResult, telegram, telegramChatId, peekThreadMessages, previewedUpdateIds, addPreviewedId, } = ctx;
26
+ const effectiveThreadId = resolveThreadId(args);
27
+ if (effectiveThreadId === undefined) {
28
+ return errorResult("Error: No active session. Call start_session first, then pass the returned threadId.");
29
+ }
30
+ const rawMessage = typeof args?.message === "string"
31
+ ? args.message
32
+ : "";
33
+ if (!rawMessage) {
34
+ return errorResult("Error: 'message' argument is required for report_progress.");
35
+ }
36
+ // Normalize literal \n sequences to actual newlines.
37
+ // Some MCP clients pass escape sequences as literal text (e.g. "foo\\nbar"
38
+ // instead of "foo\nbar"). Convert them so Telegram renders line breaks.
39
+ const normalizedMessage = rawMessage.replace(/\\n/g, "\n");
40
+ // Convert standard Markdown to Telegram MarkdownV2.
41
+ let message;
42
+ try {
43
+ message = convertMarkdown(normalizedMessage);
44
+ }
45
+ catch {
46
+ // Fall back to raw text if Markdown conversion throws.
47
+ message = normalizedMessage;
48
+ }
49
+ let sentAsPlainText = false;
50
+ const mdChunks = splitMessage(message);
51
+ try {
52
+ for (const chunk of mdChunks) {
53
+ await telegram.sendMessage(telegramChatId, chunk, "MarkdownV2", effectiveThreadId);
54
+ }
55
+ }
56
+ catch (error) {
57
+ const errMsg = errorMessage(error);
58
+ // If Telegram rejected the message due to a MarkdownV2 parse error,
59
+ // retry as plain text using the original un-converted message.
60
+ const isParseError = errMsg.includes("can't parse entities");
61
+ if (isParseError) {
62
+ try {
63
+ const plainChunks = splitMessage(rawMessage);
64
+ for (const chunk of plainChunks) {
65
+ await telegram.sendMessage(telegramChatId, chunk, undefined, effectiveThreadId);
66
+ }
67
+ sentAsPlainText = true;
68
+ }
69
+ catch (retryError) {
70
+ process.stderr.write(`Failed to send progress message via Telegram (plain fallback): ${errorMessage(retryError)}\n`);
71
+ return errorResult("Error: Failed to send progress update to Telegram even without formatting. " +
72
+ "Please check the Telegram configuration and try again.");
73
+ }
74
+ }
75
+ else {
76
+ process.stderr.write(`Failed to send progress message via Telegram: ${errMsg}\n`);
77
+ return errorResult("Error: Failed to send progress update to Telegram. " +
78
+ "Check the Telegram configuration and try again.");
79
+ }
80
+ }
81
+ // Peek at any messages the operator sent while the agent was working.
82
+ // Uses non-destructive peek so media is preserved for full delivery
83
+ // via remote_copilot_wait_for_instructions. Tracks previewed update_ids
84
+ // to prevent the same messages from appearing on repeated calls.
85
+ let pendingMessages = [];
86
+ try {
87
+ const pendingStored = peekThreadMessages(effectiveThreadId);
88
+ for (const msg of pendingStored) {
89
+ if (previewedUpdateIds.has(msg.update_id))
90
+ continue;
91
+ addPreviewedId(msg.update_id);
92
+ if (msg.message.photo && msg.message.photo.length > 0) {
93
+ pendingMessages.push(msg.message.caption
94
+ ? `[Photo received — will be downloaded when you call wait_for_instructions] ${msg.message.caption}`
95
+ : "[Photo received from operator — will be downloaded when you call wait_for_instructions]");
96
+ }
97
+ else if (msg.message.document) {
98
+ pendingMessages.push(msg.message.caption
99
+ ? `[Document: ${msg.message.document.file_name ?? "file"} — will be downloaded when you call wait_for_instructions] ${msg.message.caption}`
100
+ : `[Document received: ${msg.message.document.file_name ?? "file"} — will be downloaded when you call wait_for_instructions]`);
101
+ }
102
+ else if (msg.message.voice) {
103
+ pendingMessages.push(`[Voice message — ${msg.message.voice.duration}s — will be transcribed on next wait]`);
104
+ }
105
+ else if (msg.message.video_note) {
106
+ pendingMessages.push(`[Video note — ${msg.message.video_note.duration}s — will be analyzed on next wait]`);
107
+ }
108
+ else if (msg.message.text) {
109
+ pendingMessages.push(msg.message.text);
110
+ }
111
+ else {
112
+ pendingMessages.push("[Unsupported message type — will be shown on next wait]");
113
+ }
114
+ }
115
+ }
116
+ catch {
117
+ // Non-fatal: pending messages will still be picked up by the next
118
+ // remote_copilot_wait_for_instructions call.
119
+ }
120
+ const baseStatus = (sentAsPlainText
121
+ ? "Progress reported successfully (as plain text — formatting could not be applied)."
122
+ : "Progress reported successfully.") + getShortReminder(effectiveThreadId);
123
+ const responseText = pendingMessages.length > 0
124
+ ? `${baseStatus}\n\n` +
125
+ `While you were working, the operator sent additional message(s). ` +
126
+ `Use those messages to steer your active session: ${pendingMessages.join("\n\n")}`
127
+ : baseStatus;
128
+ return {
129
+ content: [
130
+ {
131
+ type: "text",
132
+ text: responseText,
133
+ },
134
+ ],
135
+ };
136
+ }
137
+ // ---------------------------------------------------------------------------
138
+ // hibernate
139
+ // ---------------------------------------------------------------------------
140
+ async function handleHibernate(args, ctx, extra) {
141
+ const { resolveThreadId, getShortReminder, errorResult, peekThreadMessages, checkMaintenanceFlag, checkDueTasks, generateDmnReflection, lastOperatorMessageAt, } = ctx;
142
+ const effectiveThreadId = resolveThreadId(args);
143
+ if (effectiveThreadId === undefined) {
144
+ return errorResult("Error: No active session. Call start_session first.");
145
+ }
146
+ const wakeAt = typeof args.wakeAt === "string" ? new Date(args.wakeAt).getTime() : undefined;
147
+ if (wakeAt !== undefined && isNaN(wakeAt)) {
148
+ return errorResult("Error: Invalid wakeAt timestamp. Use ISO 8601 format.");
149
+ }
150
+ // Max hibernation time: 8 hours
151
+ const MAX_HIBERNATE_MS = 8 * 60 * 60 * 1000;
152
+ const HIBERNATE_POLL_MS = 30_000; // 30s
153
+ const SSE_KEEPALIVE_INTERVAL_MS = 30_000;
154
+ const deadline = Date.now() + MAX_HIBERNATE_MS;
155
+ let lastKeepalive = Date.now();
156
+ process.stderr.write(`[hibernate] Entering hibernation. threadId=${effectiveThreadId}, wakeAt=${wakeAt ? new Date(wakeAt).toISOString() : "indefinite"}\n`);
157
+ while (Date.now() < deadline) {
158
+ // Check for operator messages (non-destructive peek)
159
+ const peeked = peekThreadMessages(effectiveThreadId);
160
+ if (peeked.length > 0) {
161
+ process.stderr.write(`[hibernate] Waking up — ${peeked.length} operator message(s) received.\n`);
162
+ // Don't consume messages — let the next wait_for_instructions call handle them
163
+ return {
164
+ content: [{
165
+ type: "text",
166
+ text: `Woke up: operator sent a message. Call wait_for_instructions now to read it.` +
167
+ getShortReminder(effectiveThreadId),
168
+ }],
169
+ };
170
+ }
171
+ // Maintenance flag: stay hibernating (don't wake) — the watcher will restart us
172
+ // This is distinct from wait_for_instructions which tells the agent to hibernate.
173
+ // Here we're already hibernating, so we just keep hibernating through the update.
174
+ const maintenanceInfo = checkMaintenanceFlag();
175
+ if (maintenanceInfo) {
176
+ process.stderr.write(`[hibernate] Maintenance flag detected — staying hibernated through update: ${maintenanceInfo}\n`);
177
+ // Skip all other checks, just keep hibernating
178
+ await new Promise((resolve) => setTimeout(resolve, HIBERNATE_POLL_MS));
179
+ continue;
180
+ }
181
+ // Check for scheduled tasks
182
+ const dueTask = checkDueTasks(effectiveThreadId, lastOperatorMessageAt, false);
183
+ if (dueTask) {
184
+ process.stderr.write(`[hibernate] Waking up — scheduled task fired: ${dueTask.task.label}\n`);
185
+ // DMN sentinel: generate dynamic first-person reflection
186
+ const taskPrompt = dueTask.prompt === "__DMN__"
187
+ ? generateDmnReflection(effectiveThreadId)
188
+ : `⏰ Woke up: scheduled task **"${dueTask.task.label}"**\n\n${dueTask.prompt}`;
189
+ return {
190
+ content: [{
191
+ type: "text",
192
+ text: taskPrompt + getShortReminder(effectiveThreadId),
193
+ }],
194
+ };
195
+ }
196
+ // Check alarm
197
+ if (wakeAt && Date.now() >= wakeAt) {
198
+ process.stderr.write(`[hibernate] Waking up — alarm reached.\n`);
199
+ return {
200
+ content: [{
201
+ type: "text",
202
+ text: `Woke up: alarm time reached (${new Date(wakeAt).toISOString()}).` +
203
+ getShortReminder(effectiveThreadId),
204
+ }],
205
+ };
206
+ }
207
+ // SSE keepalive — use the same approach as wait_for_instructions
208
+ const sinceKeepalive = Date.now() - lastKeepalive;
209
+ if (sinceKeepalive >= SSE_KEEPALIVE_INTERVAL_MS && extra?.sendNotification) {
210
+ lastKeepalive = Date.now();
211
+ try {
212
+ await extra.sendNotification({
213
+ method: "notifications/progress",
214
+ params: {
215
+ progressToken: extra.requestId,
216
+ progress: 0,
217
+ total: 0,
218
+ },
219
+ });
220
+ }
221
+ catch {
222
+ process.stderr.write(`[hibernate] SSE keepalive failed — connection lost.\n`);
223
+ return {
224
+ content: [{
225
+ type: "text",
226
+ text: "Hibernation interrupted: connection lost. Call hibernate again to resume." +
227
+ getShortReminder(effectiveThreadId),
228
+ }],
229
+ };
230
+ }
231
+ }
232
+ // Check abort signal
233
+ if (extra.signal.aborted) {
234
+ process.stderr.write(`[hibernate] SSE connection aborted during hibernation.\n`);
235
+ return {
236
+ content: [{
237
+ type: "text",
238
+ text: "Hibernation interrupted: connection closed." +
239
+ getShortReminder(effectiveThreadId),
240
+ }],
241
+ };
242
+ }
243
+ await new Promise((resolve) => setTimeout(resolve, HIBERNATE_POLL_MS));
244
+ }
245
+ // Max hibernation duration reached
246
+ process.stderr.write(`[hibernate] Max hibernation duration reached (8h).\n`);
247
+ return {
248
+ content: [{
249
+ type: "text",
250
+ text: "Woke up: maximum hibernation duration reached (8 hours)." +
251
+ getShortReminder(effectiveThreadId),
252
+ }],
253
+ };
254
+ }
255
+ //# sourceMappingURL=session-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-tools.js","sourceRoot":"","sources":["../../src/tools/session-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * start_session tool handler extracted from index.ts.
3
+ *
4
+ * Creates or resumes a Telegram forum topic for the current MCP session,
5
+ * bootstraps memory, auto-schedules DMN reflection, and returns the
6
+ * session greeting with reminders.
7
+ */
8
+ import { type initMemoryDb } from "../memory.js";
9
+ import type { TelegramClient } from "../telegram.js";
10
+ import type { AppConfig } from "../types.js";
11
+ export interface StartSessionContext {
12
+ /** Mutable per-session state — the handler writes directly into this. */
13
+ session: {
14
+ currentThreadId: number | undefined;
15
+ sessionStartedAt: number;
16
+ waitCallCount: number;
17
+ lastToolCallAt: number;
18
+ deadSessionAlerted: boolean;
19
+ toolCallsSinceLastDelivery: number;
20
+ previewedUpdateIds: Set<number>;
21
+ lastOperatorMessageAt: number;
22
+ lastConsolidationAt: number;
23
+ };
24
+ telegram: TelegramClient;
25
+ telegramChatId: string;
26
+ config: AppConfig;
27
+ getMemoryDb: () => ReturnType<typeof initMemoryDb>;
28
+ getReminders: (threadId: number | undefined, driveActive: boolean, sessionStartedAt: number, autonomousMode: boolean) => string;
29
+ /** MCP session ID for this connection (from transport). */
30
+ getMcpSessionId?: () => string | undefined;
31
+ /** Close the transport — used for session registration. */
32
+ closeTransport?: () => void;
33
+ }
34
+ type ToolResult = {
35
+ content: Array<{
36
+ type: string;
37
+ text: string;
38
+ }>;
39
+ isError?: boolean;
40
+ };
41
+ export declare function handleStartSession(args: Record<string, unknown>, ctx: StartSessionContext): Promise<ToolResult>;
42
+ export {};
43
+ //# sourceMappingURL=start-session-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-session-tool.d.ts","sourceRoot":"","sources":["../../src/tools/start-session-tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AASpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQ7C,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,OAAO,EAAE;QACP,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;QACpC,gBAAgB,EAAE,MAAM,CAAC;QACzB,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,0BAA0B,EAAE,MAAM,CAAC;QACnC,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,qBAAqB,EAAE,MAAM,CAAC;QAC9B,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IAEF,QAAQ,EAAE,cAAc,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,CAAC;IAClB,WAAW,EAAE,MAAM,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACnD,YAAY,EAAE,CACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,WAAW,EAAE,OAAO,EACpB,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,OAAO,KACpB,MAAM,CAAC;IAEZ,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC3C,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,KAAK,UAAU,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAMxF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,mBAAmB,GACvB,OAAO,CAAC,UAAU,CAAC,CA0LrB"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * start_session tool handler extracted from index.ts.
3
+ *
4
+ * Creates or resumes a Telegram forum topic for the current MCP session,
5
+ * bootstraps memory, auto-schedules DMN reflection, and returns the
6
+ * session greeting with reminders.
7
+ */
8
+ import { convertMarkdown } from "../markdown.js";
9
+ import { assembleBootstrap } from "../memory.js";
10
+ import { addSchedule, generateTaskId, listSchedules, purgeSchedules } from "../scheduler.js";
11
+ import { lookupSession, persistSession, purgeOtherSessions, registerMcpSession, removeSession, } from "../sessions.js";
12
+ import { errorMessage, errorResult } from "../utils.js";
13
+ import { readThreadMessages } from "../dispatcher.js";
14
+ // ---------------------------------------------------------------------------
15
+ // Handler
16
+ // ---------------------------------------------------------------------------
17
+ export async function handleStartSession(args, ctx) {
18
+ const { session, telegram, telegramChatId: TELEGRAM_CHAT_ID, config, getMemoryDb } = ctx;
19
+ session.sessionStartedAt = Date.now();
20
+ const typedArgs = args;
21
+ const rawThreadId = typedArgs.threadId;
22
+ const explicitThreadId = typeof rawThreadId === "number" ? rawThreadId
23
+ : typeof rawThreadId === "string" ? (Number.isFinite(Number(rawThreadId)) ? Number(rawThreadId) : undefined)
24
+ : undefined;
25
+ const customName = typeof typedArgs.name === "string" && typedArgs.name.trim()
26
+ ? typedArgs.name.trim()
27
+ : undefined;
28
+ // When creating a new session (no threadId), name is mandatory.
29
+ if (explicitThreadId === undefined && !customName) {
30
+ return {
31
+ content: [
32
+ {
33
+ type: "text",
34
+ text: "Error: sessionName is required when creating a new session. Provide a descriptive name for the session.",
35
+ },
36
+ ],
37
+ isError: true,
38
+ };
39
+ }
40
+ // Determine the thread to use:
41
+ // 1. Explicit threadId beats everything.
42
+ // 2. A known name looks up the persisted mapping — resume if found.
43
+ // 3. Otherwise create a new topic.
44
+ let resolvedPreexisting = false;
45
+ if (explicitThreadId !== undefined) {
46
+ session.currentThreadId = explicitThreadId;
47
+ // If a name was also supplied, keep the mapping up to date.
48
+ if (customName)
49
+ persistSession(TELEGRAM_CHAT_ID, customName, explicitThreadId);
50
+ resolvedPreexisting = true;
51
+ }
52
+ else if (customName !== undefined) {
53
+ const stored = lookupSession(TELEGRAM_CHAT_ID, customName);
54
+ if (stored !== undefined) {
55
+ session.currentThreadId = stored;
56
+ resolvedPreexisting = true;
57
+ }
58
+ }
59
+ if (resolvedPreexisting) {
60
+ // Drain any stale messages from the thread file so they aren't
61
+ // re-delivered in the next wait_for_instructions call.
62
+ const stale = readThreadMessages(session.currentThreadId);
63
+ if (stale.length > 0) {
64
+ process.stderr.write(`[start_session] Drained ${stale.length} stale message(s) from thread ${session.currentThreadId}.\n`);
65
+ // Notify the operator that stale messages were discarded.
66
+ try {
67
+ const notice = convertMarkdown(`\u26A0\uFE0F **${stale.length} message(s) from before the session resumed were discarded.** ` +
68
+ `If you sent instructions while the agent was offline, please resend them.`);
69
+ await telegram.sendMessage(TELEGRAM_CHAT_ID, notice, "MarkdownV2", session.currentThreadId);
70
+ }
71
+ catch { /* non-fatal */ }
72
+ }
73
+ // Resume mode: verify the thread is still alive by sending a message.
74
+ // If the topic was deleted, drop the cached mapping and fall through to
75
+ // create a new topic.
76
+ try {
77
+ // Use plain text for probe — avoids MarkdownV2 parsing failures being mistaken for dead threads
78
+ await telegram.sendMessage(TELEGRAM_CHAT_ID, "\u{1F504} Session resumed. Continuing in this thread.", undefined, session.currentThreadId);
79
+ }
80
+ catch (err) {
81
+ const errMsg = errorMessage(err);
82
+ process.stderr.write(`[start_session] Probe failed for thread ${session.currentThreadId} in chat ${TELEGRAM_CHAT_ID}: ${errMsg}\n`);
83
+ // Telegram returns "Bad Request: message thread not found" or
84
+ // "Bad Request: the topic was closed" for deleted/closed topics.
85
+ const isThreadGone = /thread not found|topic.*(closed|deleted|not found)/i.test(errMsg);
86
+ if (isThreadGone) {
87
+ process.stderr.write(`[start_session] Cached thread ${session.currentThreadId} is gone (${errMsg}). Creating new topic.\n`);
88
+ // Drop the stale mapping and purge any scheduled tasks.
89
+ if (session.currentThreadId !== undefined)
90
+ purgeSchedules(session.currentThreadId);
91
+ if (customName)
92
+ removeSession(TELEGRAM_CHAT_ID, customName);
93
+ resolvedPreexisting = false;
94
+ session.currentThreadId = undefined;
95
+ }
96
+ // Other errors (network, etc.) are non-fatal — proceed anyway.
97
+ }
98
+ }
99
+ if (!resolvedPreexisting) {
100
+ // New session: create a dedicated forum topic.
101
+ const topicName = customName ??
102
+ `Copilot — ${new Date().toLocaleString("en-GB", {
103
+ day: "2-digit", month: "short", year: "numeric",
104
+ hour: "2-digit", minute: "2-digit", hour12: false,
105
+ })}`;
106
+ try {
107
+ const topic = await telegram.createForumTopic(TELEGRAM_CHAT_ID, topicName);
108
+ session.currentThreadId = topic.message_thread_id;
109
+ // Persist so the same name resumes this thread next time.
110
+ persistSession(TELEGRAM_CHAT_ID, topicName, session.currentThreadId);
111
+ }
112
+ catch (err) {
113
+ // Forum topics not available (e.g. plain group or DM) — cannot proceed
114
+ // without thread isolation. Return an error so the agent knows.
115
+ return errorResult(`Error: Could not create forum topic: ${errorMessage(err)}. ` +
116
+ "Ensure the Telegram chat is a forum supergroup with the bot as admin with can_manage_topics right.");
117
+ }
118
+ try {
119
+ const greeting = convertMarkdown("# 🤖 Remote Copilot Ready\n\n" +
120
+ "Your AI assistant is online and listening.\n\n" +
121
+ "**Send your instructions** and I'll get to work — " +
122
+ "I'll keep you posted on progress as I go.");
123
+ await telegram.sendMessage(TELEGRAM_CHAT_ID, greeting, "MarkdownV2", session.currentThreadId);
124
+ }
125
+ catch {
126
+ // Non-fatal.
127
+ }
128
+ }
129
+ const threadNote = session.currentThreadId !== undefined
130
+ ? ` Thread ID: ${session.currentThreadId} (pass this to start_session as threadId to resume this topic later).`
131
+ : "";
132
+ // Auto-bootstrap memory
133
+ let memoryBriefing = "";
134
+ try {
135
+ const db = getMemoryDb();
136
+ if (session.currentThreadId !== undefined) {
137
+ memoryBriefing = "\n\n" + assembleBootstrap(db, session.currentThreadId);
138
+ }
139
+ }
140
+ catch (e) {
141
+ memoryBriefing = "\n\n_Memory system unavailable._";
142
+ }
143
+ // Purge stale MCP sessions for this thread (from before a server restart)
144
+ // and register the current session.
145
+ if (session.currentThreadId !== undefined) {
146
+ const sid = ctx.getMcpSessionId?.();
147
+ const purged = purgeOtherSessions(session.currentThreadId, sid);
148
+ if (purged > 0) {
149
+ process.stderr.write(`[start_session] Purged ${purged} stale MCP session(s) for thread ${session.currentThreadId}.\n`);
150
+ }
151
+ if (sid && ctx.closeTransport) {
152
+ registerMcpSession(session.currentThreadId, sid, ctx.closeTransport);
153
+ }
154
+ }
155
+ // Auto-schedule DMN reflection task if not already present.
156
+ // This fires after 4 hours of operator silence, delivering a
157
+ // first-person introspection prompt sourced from memory.
158
+ // Only create on active thread — purge stale DMN tasks from other threads
159
+ // to avoid every thread accumulating reflection tasks.
160
+ if (config.AUTONOMOUS_MODE && session.currentThreadId !== undefined) {
161
+ const existingTasks = listSchedules(session.currentThreadId);
162
+ const hasDmn = existingTasks.some(t => t.label === "dmn-reflection");
163
+ if (!hasDmn) {
164
+ addSchedule({
165
+ id: generateTaskId(),
166
+ threadId: session.currentThreadId,
167
+ prompt: "__DMN__", // Sentinel — handler generates dynamic content
168
+ label: "dmn-reflection",
169
+ afterIdleMinutes: 240, // 4 hours
170
+ oneShot: false,
171
+ createdAt: new Date().toISOString(),
172
+ });
173
+ process.stderr.write(`[start_session] Auto-scheduled DMN reflection task for thread ${session.currentThreadId}.\n`);
174
+ }
175
+ }
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text",
180
+ text: `Session ${resolvedPreexisting ? "resumed" : "started"}.${threadNote}` +
181
+ ` Call the remote_copilot_wait_for_instructions tool next.` +
182
+ memoryBriefing +
183
+ ctx.getReminders(session.currentThreadId, false, session.sessionStartedAt, config.AUTONOMOUS_MODE),
184
+ },
185
+ ],
186
+ };
187
+ }
188
+ //# sourceMappingURL=start-session-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-session-tool.js","sourceRoot":"","sources":["../../src/tools/start-session-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Utility tool handlers extracted from index.ts.
3
+ *
4
+ * Handles: send_file, send_voice, schedule_wake_up, get_version, get_usage_stats
5
+ */
6
+ import type { TelegramClient } from "../telegram.js";
7
+ import type { AppConfig } from "../types.js";
8
+ /** Closure-bound helpers passed by the caller (index.ts createMcpServer). */
9
+ export interface UtilityToolContext {
10
+ resolveThreadId: (args: Record<string, unknown>) => number | undefined;
11
+ getShortReminder: (threadId: number | undefined) => string;
12
+ errorResult: (msg: string) => {
13
+ content: Array<{
14
+ type: string;
15
+ text: string;
16
+ }>;
17
+ isError: true;
18
+ };
19
+ telegram: TelegramClient;
20
+ config: AppConfig;
21
+ sessionStartedAt: number;
22
+ waitCallCount: number;
23
+ toolCallsSinceLastDelivery: number;
24
+ }
25
+ type ToolResult = {
26
+ content: Array<{
27
+ type: string;
28
+ text: string;
29
+ }>;
30
+ isError?: boolean;
31
+ };
32
+ export declare function handleUtilityTool(name: string, args: Record<string, unknown>, ctx: UtilityToolContext): Promise<ToolResult>;
33
+ export {};
34
+ //# sourceMappingURL=utility-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utility-tools.d.ts","sourceRoot":"","sources":["../../src/tools/utility-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IACvE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,CAAC;IAC3D,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,OAAO,EAAE,IAAI,CAAA;KAAE,CAAC;IAChG,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,EAAE,SAAS,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,0BAA0B,EAAE,MAAM,CAAC;CACpC;AAED,KAAK,UAAU,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAMxF,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC,UAAU,CAAC,CAerB"}