@geminixiang/mama 0.1.9 → 0.2.0-beta.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.
- package/README.md +173 -16
- package/dist/adapter.d.ts +8 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +1 -0
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +12 -0
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +86 -10
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +48 -28
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/tools/attach.d.ts.map +1 -1
- package/dist/adapters/slack/tools/attach.js +4 -2
- package/dist/adapters/slack/tools/attach.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +6 -3
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +115 -63
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts +2 -2
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +92 -65
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +214 -39
- package/dist/agent.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +46 -13
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +15 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -2
- package/dist/context.js.map +1 -1
- package/dist/events.d.ts +10 -5
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +44 -10
- package/dist/events.js.map +1 -1
- package/dist/instrument.d.ts +2 -0
- package/dist/instrument.d.ts.map +1 -0
- package/dist/instrument.js +7 -0
- package/dist/instrument.js.map +1 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +6 -5
- package/dist/log.js.map +1 -1
- package/dist/main.d.ts +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +137 -59
- package/dist/main.js.map +1 -1
- package/dist/sandbox.d.ts +7 -1
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +127 -27
- package/dist/sandbox.js.map +1 -1
- package/dist/sentry.d.ts +31 -0
- package/dist/sentry.d.ts.map +1 -0
- package/dist/sentry.js +205 -0
- package/dist/sentry.js.map +1 -0
- package/dist/session-store.d.ts +76 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +189 -0
- package/dist/session-store.js.map +1 -0
- package/package.json +13 -12
package/dist/log.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"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","sourcesContent":["import { Logging } from \"@google-cloud/logging\";\nimport { Writable } from \"node:stream\";\nimport chalk from \"chalk\";\nimport pino from \"pino\";\n\nconst PINO_TO_GCP: Record<number, string> = {\n 10: \"DEBUG\",\n 20: \"DEBUG\",\n 30: \"INFO\",\n 40: \"WARNING\",\n 50: \"ERROR\",\n 60: \"CRITICAL\",\n};\n\nfunction createGcpStream(): Writable {\n const log = new Logging().log(\"mama\");\n return new Writable({\n write(chunk, _encoding, callback) {\n try {\n const line = chunk.toString().trim();\n if (line) {\n const { level, time, pid: _pid, hostname: _hostname, msg, ...rest } = JSON.parse(line);\n const entry = log.entry(\n { severity: PINO_TO_GCP[level] ?? \"DEFAULT\", timestamp: new Date(time) },\n { message: msg, ...rest },\n );\n log.write(entry).catch((err) => console.error(\"GCP log write failed:\", err));\n }\n } catch {\n // ignore parse errors\n }\n callback();\n },\n });\n}\n\nexport interface LogContext {\n channelId: string;\n userName?: string;\n channelName?: string; // For display like #dev-team vs C16HET4EQ\n}\n\nexport interface LogConfig {\n logFormat?: \"console\" | \"json\";\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\";\n}\n\nlet logger: pino.Logger | null = null;\n\nexport function initLogger(config?: LogConfig): void {\n if (logger) return;\n\n const format = config?.logFormat ?? \"console\";\n const level = config?.logLevel ?? \"info\";\n\n if (format === \"json\") {\n try {\n logger = pino({ level }, createGcpStream());\n console.log(`📝 GCP logging enabled (level: ${level})`);\n } catch (err) {\n console.warn(\"⚠️ Failed to init GCP logger, JSON logging disabled:\", err);\n }\n }\n}\n\n/** Only for use in tests. */\nexport function __resetLoggerForTest(): void {\n logger = null;\n}\n\nfunction ctxFields(ctx: LogContext): Record<string, string> {\n const out: Record<string, string> = { channel: ctx.channelId };\n if (ctx.userName) out.user = ctx.userName;\n if (ctx.channelName) out.channelName = ctx.channelName;\n return out;\n}\n\nfunction timestamp(): string {\n const now = new Date();\n const hh = String(now.getHours()).padStart(2, \"0\");\n const mm = String(now.getMinutes()).padStart(2, \"0\");\n const ss = String(now.getSeconds()).padStart(2, \"0\");\n return `[${hh}:${mm}:${ss}]`;\n}\n\nfunction formatContext(ctx: LogContext): string {\n // DMs: [DM:username]\n // Channels: [#channel-name:username] or [C16HET4EQ:username] if no name\n if (ctx.channelId.startsWith(\"D\")) {\n return `[DM:${ctx.userName || ctx.channelId}]`;\n }\n const channel = ctx.channelName || ctx.channelId;\n const user = ctx.userName || \"unknown\";\n return `[${channel.startsWith(\"#\") ? channel : `#${channel}`}:${user}]`;\n}\n\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return `${text.substring(0, maxLen)}\\n(truncated at ${maxLen} chars)`;\n}\n\nfunction formatToolArgs(args: Record<string, unknown>): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(args)) {\n // Skip the label - it's already shown in the tool name\n if (key === \"label\") continue;\n\n // For read tool, format path with offset/limit\n if (key === \"path\" && typeof value === \"string\") {\n const offset = args.offset as number | undefined;\n const limit = args.limit as number | undefined;\n if (offset !== undefined && limit !== undefined) {\n lines.push(`${value}:${offset}-${offset + limit}`);\n } else {\n lines.push(value);\n }\n continue;\n }\n\n // Skip offset/limit since we already handled them\n if (key === \"offset\" || key === \"limit\") continue;\n\n // For other values, format them\n if (typeof value === \"string\") {\n // Multi-line strings get indented\n if (value.includes(\"\\n\")) {\n lines.push(value);\n } else {\n lines.push(value);\n }\n } else {\n lines.push(JSON.stringify(value));\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n// User messages\nexport function logUserMessage(ctx: LogContext, text: string): void {\n if (logger) logger.info({ event: \"user_message\", ...ctxFields(ctx), text }, text);\n console.log(chalk.green(`${timestamp()} ${formatContext(ctx)} ${text}`));\n}\n\n// Tool execution\nexport function logToolStart(\n ctx: LogContext,\n toolName: string,\n label: string,\n args: Record<string, unknown>,\n): void {\n if (logger)\n logger.debug(\n { event: \"tool_start\", ...ctxFields(ctx), tool: toolName, label, args },\n `${toolName}: ${label}`,\n );\n const formattedArgs = formatToolArgs(args);\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ↳ ${toolName}: ${label}`));\n if (formattedArgs) {\n // Indent the args\n const indented = formattedArgs\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n }\n}\n\nexport function logToolSuccess(\n ctx: LogContext,\n toolName: string,\n durationMs: number,\n result: string,\n): void {\n if (logger)\n logger.debug(\n { event: \"tool_success\", ...ctxFields(ctx), tool: toolName, durationMs, result },\n `${toolName} completed`,\n );\n const duration = (durationMs / 1000).toFixed(1);\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ✓ ${toolName} (${duration}s)`));\n\n const truncated = truncate(result, 1000);\n if (truncated) {\n const indented = truncated\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n }\n}\n\nexport function logToolError(\n ctx: LogContext,\n toolName: string,\n durationMs: number,\n error: string,\n): void {\n if (logger)\n logger.warn(\n { event: \"tool_error\", ...ctxFields(ctx), tool: toolName, durationMs, error },\n `${toolName} failed`,\n );\n const duration = (durationMs / 1000).toFixed(1);\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ✗ ${toolName} (${duration}s)`));\n\n const truncated = truncate(error, 1000);\n const indented = truncated\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n}\n\n// Response streaming\nexport function logResponseStart(ctx: LogContext): void {\n if (logger) logger.debug({ event: \"response_start\", ...ctxFields(ctx) }, \"Streaming response\");\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} → Streaming response...`));\n}\n\nexport function logThinking(ctx: LogContext, thinking: string): void {\n if (logger) logger.debug({ event: \"thinking\", ...ctxFields(ctx), text: thinking }, \"Thinking\");\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} 💭 Thinking`));\n const truncated = truncate(thinking, 1000);\n const indented = truncated\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n}\n\nexport function logResponse(ctx: LogContext, text: string): void {\n if (logger) logger.info({ event: \"response\", ...ctxFields(ctx), text }, \"Response\");\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} 💬 Response`));\n const truncated = truncate(text, 1000);\n const indented = truncated\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n}\n\n// Attachments\nexport function logDownloadStart(ctx: LogContext, filename: string, localPath: string): void {\n if (logger)\n logger.debug(\n { event: \"download_start\", ...ctxFields(ctx), filename, localPath },\n `Downloading ${filename}`,\n );\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ↓ Downloading attachment`));\n console.log(chalk.dim(` ${filename} → ${localPath}`));\n}\n\nexport function logDownloadSuccess(ctx: LogContext, sizeKB: number): void {\n if (logger)\n logger.info(\n { event: \"download_success\", ...ctxFields(ctx), sizeKB },\n `Downloaded (${sizeKB} KB)`,\n );\n console.log(\n chalk.yellow(\n `${timestamp()} ${formatContext(ctx)} ✓ Downloaded (${sizeKB.toLocaleString()} KB)`,\n ),\n );\n}\n\nexport function logDownloadError(ctx: LogContext, filename: string, error: string): void {\n if (logger)\n logger.warn(\n { event: \"download_error\", ...ctxFields(ctx), filename, error },\n `Download failed: ${filename}`,\n );\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ✗ Download failed`));\n console.log(chalk.dim(` ${filename}: ${error}`));\n}\n\n// Control\nexport function logStopRequest(ctx: LogContext): void {\n if (logger) logger.info({ event: \"stop_request\", ...ctxFields(ctx) }, \"Stop requested\");\n console.log(chalk.green(`${timestamp()} ${formatContext(ctx)} stop`));\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ⊗ Stop requested - aborting`));\n}\n\n// System\nexport function logInfo(message: string): void {\n if (logger) logger.info({ event: \"info\" }, message);\n console.log(chalk.blue(`${timestamp()} [system] ${message}`));\n}\n\nexport function logWarning(message: string, details?: string): void {\n if (logger) logger.warn({ event: \"warning\", ...(details ? { details } : {}) }, message);\n console.log(chalk.yellow(`${timestamp()} [system] ⚠ ${message}`));\n if (details) {\n const indented = details\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n }\n}\n\nexport function logAgentError(ctx: LogContext | \"system\", error: string): void {\n if (logger) {\n const extra = ctx === \"system\" ? { error } : { ...ctxFields(ctx), error };\n logger.error({ event: \"agent_error\", ...extra }, \"Agent error\");\n }\n const context = ctx === \"system\" ? \"[system]\" : formatContext(ctx);\n console.log(chalk.yellow(`${timestamp()} ${context} ✗ Agent error`));\n const indented = error\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n}\n\n// Usage summary\nexport function logUsageSummary(\n ctx: LogContext,\n usage: {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n cost: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };\n },\n contextTokens?: number,\n contextWindow?: number,\n): string {\n const formatTokens = (count: number): string => {\n if (count < 1000) return count.toString();\n if (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n if (count < 1000000) return `${Math.round(count / 1000)}k`;\n return `${(count / 1000000).toFixed(1)}M`;\n };\n\n const lines: string[] = [];\n lines.push(\"*Usage Summary*\");\n lines.push(`Tokens: ${usage.input.toLocaleString()} in, ${usage.output.toLocaleString()} out`);\n if (usage.cacheRead > 0 || usage.cacheWrite > 0) {\n lines.push(\n `Cache: ${usage.cacheRead.toLocaleString()} read, ${usage.cacheWrite.toLocaleString()} write`,\n );\n }\n if (contextTokens && contextWindow) {\n const contextPercent = ((contextTokens / contextWindow) * 100).toFixed(1);\n lines.push(\n `Context: ${formatTokens(contextTokens)} / ${formatTokens(contextWindow)} (${contextPercent}%)`,\n );\n }\n lines.push(\n `Cost: $${usage.cost.input.toFixed(4)} in, $${usage.cost.output.toFixed(4)} out` +\n (usage.cacheRead > 0 || usage.cacheWrite > 0\n ? `, $${usage.cost.cacheRead.toFixed(4)} cache read, $${usage.cost.cacheWrite.toFixed(4)} cache write`\n : \"\"),\n );\n lines.push(`*Total: $${usage.cost.total.toFixed(4)}*`);\n\n const summary = lines.join(\"\\n\");\n\n // Log to console\n if (logger) {\n logger.info(\n {\n event: \"usage\",\n ...ctxFields(ctx),\n tokensIn: usage.input,\n tokensOut: usage.output,\n cacheRead: usage.cacheRead,\n cacheWrite: usage.cacheWrite,\n cost: usage.cost.total,\n },\n `Usage: $${usage.cost.total.toFixed(4)}`,\n );\n }\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} 💰 Usage`));\n console.log(\n chalk.dim(\n ` ${usage.input.toLocaleString()} in + ${usage.output.toLocaleString()} out` +\n (usage.cacheRead > 0 || usage.cacheWrite > 0\n ? ` (${usage.cacheRead.toLocaleString()} cache read, ${usage.cacheWrite.toLocaleString()} cache write)`\n : \"\") +\n ` = $${usage.cost.total.toFixed(4)}`,\n ),\n );\n\n return summary;\n}\n\n// Startup (no context needed)\nexport function logStartup(workingDir: string, sandbox: string): void {\n if (logger) logger.info({ event: \"startup\", workingDir, sandbox }, \"Starting mama\");\n console.log(\"Starting mama...\");\n console.log(` Working directory: ${workingDir}`);\n console.log(` Sandbox: ${sandbox}`);\n}\n\nexport function logConnected(): void {\n if (logger) logger.info({ event: \"connected\" }, \"Mama connected and listening\");\n console.log(\"⚡️ Mama connected and listening!\");\n console.log(\"\");\n}\n\nexport function logDisconnected(): void {\n if (logger) logger.info({ event: \"disconnected\" }, \"Mama disconnected\");\n console.log(\"Mama disconnected.\");\n}\n\n// Backfill\nexport function logBackfillStart(channelCount: number): void {\n if (logger)\n logger.info({ event: \"backfill_start\", channelCount }, `Backfilling ${channelCount} channels`);\n console.log(chalk.blue(`${timestamp()} [system] Backfilling ${channelCount} channels...`));\n}\n\nexport function logBackfillChannel(channelName: string, messageCount: number): void {\n if (logger)\n logger.debug(\n { event: \"backfill_channel\", channelName, messageCount },\n `#${channelName}: ${messageCount} messages`,\n );\n console.log(chalk.blue(`${timestamp()} [system] #${channelName}: ${messageCount} messages`));\n}\n\nexport function logBackfillComplete(totalMessages: number, durationMs: number): void {\n if (logger)\n logger.info(\n { event: \"backfill_complete\", totalMessages, durationMs },\n `Backfill complete: ${totalMessages} messages`,\n );\n const duration = (durationMs / 1000).toFixed(1);\n console.log(\n chalk.blue(\n `${timestamp()} [system] Backfill complete: ${totalMessages} messages in ${duration}s`,\n ),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"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","sourcesContent":["import { Logging } from \"@google-cloud/logging\";\nimport { Writable } from \"node:stream\";\nimport chalk from \"chalk\";\nimport pino from \"pino\";\n\nconst PINO_TO_GCP: Record<number, string> = {\n 10: \"DEBUG\",\n 20: \"DEBUG\",\n 30: \"INFO\",\n 40: \"WARNING\",\n 50: \"ERROR\",\n 60: \"CRITICAL\",\n};\n\nfunction createGcpStream(): Writable {\n const log = new Logging().log(\"mama\");\n return new Writable({\n write(chunk, _encoding, callback) {\n try {\n const line = chunk.toString().trim();\n if (line) {\n const { level, time, pid: _pid, hostname: _hostname, msg, ...rest } = JSON.parse(line);\n const entry = log.entry(\n { severity: PINO_TO_GCP[level] ?? \"DEFAULT\", timestamp: new Date(time) },\n { message: msg, ...rest },\n );\n log.write(entry).catch((err) => console.error(\"GCP log write failed:\", err));\n }\n } catch {\n // ignore parse errors\n }\n callback();\n },\n });\n}\n\nexport interface LogContext {\n channelId: string;\n userName?: string;\n channelName?: string; // For display like #dev-team vs C16HET4EQ\n sessionId?: string;\n}\n\nexport interface LogConfig {\n logFormat?: \"console\" | \"json\";\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\";\n}\n\nlet logger: pino.Logger | null = null;\n\nexport function initLogger(config?: LogConfig): void {\n if (logger) return;\n\n const format = config?.logFormat ?? \"console\";\n const level = config?.logLevel ?? \"info\";\n\n if (format === \"json\") {\n try {\n logger = pino({ level }, createGcpStream());\n console.log(`📝 GCP logging enabled (level: ${level})`);\n } catch (err) {\n console.warn(\"⚠️ Failed to init GCP logger, JSON logging disabled:\", err);\n }\n }\n}\n\n/** Only for use in tests. */\nexport function __resetLoggerForTest(): void {\n logger = null;\n}\n\nfunction ctxFields(ctx: LogContext): Record<string, string> {\n const out: Record<string, string> = { channel: ctx.channelId };\n if (ctx.userName) out.user = ctx.userName;\n if (ctx.channelName) out.channelName = ctx.channelName;\n if (ctx.sessionId) out.sessionId = ctx.sessionId;\n return out;\n}\n\nfunction timestamp(): string {\n const now = new Date();\n const hh = String(now.getHours()).padStart(2, \"0\");\n const mm = String(now.getMinutes()).padStart(2, \"0\");\n const ss = String(now.getSeconds()).padStart(2, \"0\");\n return `[${hh}:${mm}:${ss}]`;\n}\n\nfunction formatContext(ctx: LogContext): string {\n const session = ctx.sessionId ? `:${ctx.sessionId}` : \"\";\n if (ctx.channelId.startsWith(\"D\")) {\n return `[DM:${ctx.userName || ctx.channelId}${session}]`;\n }\n const channel = ctx.channelName || ctx.channelId;\n const user = ctx.userName || \"unknown\";\n return `[${channel.startsWith(\"#\") ? channel : `#${channel}`}:${user}${session}]`;\n}\n\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return `${text.substring(0, maxLen)}\\n(truncated at ${maxLen} chars)`;\n}\n\nfunction formatToolArgs(args: Record<string, unknown>): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(args)) {\n // Skip the label - it's already shown in the tool name\n if (key === \"label\") continue;\n\n // For read tool, format path with offset/limit\n if (key === \"path\" && typeof value === \"string\") {\n const offset = args.offset as number | undefined;\n const limit = args.limit as number | undefined;\n if (offset !== undefined && limit !== undefined) {\n lines.push(`${value}:${offset}-${offset + limit}`);\n } else {\n lines.push(value);\n }\n continue;\n }\n\n // Skip offset/limit since we already handled them\n if (key === \"offset\" || key === \"limit\") continue;\n\n // For other values, format them\n if (typeof value === \"string\") {\n // Multi-line strings get indented\n if (value.includes(\"\\n\")) {\n lines.push(value);\n } else {\n lines.push(value);\n }\n } else {\n lines.push(JSON.stringify(value));\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n// User messages\nexport function logUserMessage(ctx: LogContext, text: string): void {\n if (logger) logger.info({ event: \"user_message\", ...ctxFields(ctx), text }, text);\n console.log(chalk.green(`${timestamp()} ${formatContext(ctx)} ${text}`));\n}\n\n// Tool execution\nexport function logToolStart(\n ctx: LogContext,\n toolName: string,\n label: string,\n args: Record<string, unknown>,\n): void {\n if (logger)\n logger.debug(\n { event: \"tool_start\", ...ctxFields(ctx), tool: toolName, label, args },\n `${toolName}: ${label}`,\n );\n const formattedArgs = formatToolArgs(args);\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ↳ ${toolName}: ${label}`));\n if (formattedArgs) {\n // Indent the args\n const indented = formattedArgs\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n }\n}\n\nexport function logToolSuccess(\n ctx: LogContext,\n toolName: string,\n durationMs: number,\n result: string,\n): void {\n if (logger)\n logger.debug(\n { event: \"tool_success\", ...ctxFields(ctx), tool: toolName, durationMs, result },\n `${toolName} completed`,\n );\n const duration = (durationMs / 1000).toFixed(1);\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ✓ ${toolName} (${duration}s)`));\n\n const truncated = truncate(result, 1000);\n if (truncated) {\n const indented = truncated\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n }\n}\n\nexport function logToolError(\n ctx: LogContext,\n toolName: string,\n durationMs: number,\n error: string,\n): void {\n if (logger)\n logger.warn(\n { event: \"tool_error\", ...ctxFields(ctx), tool: toolName, durationMs, error },\n `${toolName} failed`,\n );\n const duration = (durationMs / 1000).toFixed(1);\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ✗ ${toolName} (${duration}s)`));\n\n const truncated = truncate(error, 1000);\n const indented = truncated\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n}\n\n// Response streaming\nexport function logResponseStart(ctx: LogContext): void {\n if (logger) logger.debug({ event: \"response_start\", ...ctxFields(ctx) }, \"Streaming response\");\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} → Streaming response...`));\n}\n\nexport function logThinking(ctx: LogContext, thinking: string): void {\n if (logger) logger.debug({ event: \"thinking\", ...ctxFields(ctx), text: thinking }, \"Thinking\");\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} 💭 Thinking`));\n const truncated = truncate(thinking, 1000);\n const indented = truncated\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n}\n\nexport function logResponse(ctx: LogContext, text: string): void {\n if (logger) logger.info({ event: \"response\", ...ctxFields(ctx), text }, \"Response\");\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} 💬 Response`));\n const truncated = truncate(text, 1000);\n const indented = truncated\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n}\n\n// Attachments\nexport function logDownloadStart(ctx: LogContext, filename: string, localPath: string): void {\n if (logger)\n logger.debug(\n { event: \"download_start\", ...ctxFields(ctx), filename, localPath },\n `Downloading ${filename}`,\n );\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ↓ Downloading attachment`));\n console.log(chalk.dim(` ${filename} → ${localPath}`));\n}\n\nexport function logDownloadSuccess(ctx: LogContext, sizeKB: number): void {\n if (logger)\n logger.info(\n { event: \"download_success\", ...ctxFields(ctx), sizeKB },\n `Downloaded (${sizeKB} KB)`,\n );\n console.log(\n chalk.yellow(\n `${timestamp()} ${formatContext(ctx)} ✓ Downloaded (${sizeKB.toLocaleString()} KB)`,\n ),\n );\n}\n\nexport function logDownloadError(ctx: LogContext, filename: string, error: string): void {\n if (logger)\n logger.warn(\n { event: \"download_error\", ...ctxFields(ctx), filename, error },\n `Download failed: ${filename}`,\n );\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ✗ Download failed`));\n console.log(chalk.dim(` ${filename}: ${error}`));\n}\n\n// Control\nexport function logStopRequest(ctx: LogContext): void {\n if (logger) logger.info({ event: \"stop_request\", ...ctxFields(ctx) }, \"Stop requested\");\n console.log(chalk.green(`${timestamp()} ${formatContext(ctx)} stop`));\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} ⊗ Stop requested - aborting`));\n}\n\n// System\nexport function logInfo(message: string): void {\n if (logger) logger.info({ event: \"info\" }, message);\n console.log(chalk.blue(`${timestamp()} [system] ${message}`));\n}\n\nexport function logWarning(message: string, details?: string): void {\n if (logger) logger.warn({ event: \"warning\", ...(details ? { details } : {}) }, message);\n console.log(chalk.yellow(`${timestamp()} [system] ⚠ ${message}`));\n if (details) {\n const indented = details\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n }\n}\n\nexport function logAgentError(ctx: LogContext | \"system\", error: string): void {\n if (logger) {\n const extra = ctx === \"system\" ? { error } : { ...ctxFields(ctx), error };\n logger.error({ event: \"agent_error\", ...extra }, \"Agent error\");\n }\n const context = ctx === \"system\" ? \"[system]\" : formatContext(ctx);\n console.log(chalk.yellow(`${timestamp()} ${context} ✗ Agent error`));\n const indented = error\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n console.log(chalk.dim(indented));\n}\n\n// Usage summary\nexport function logUsageSummary(\n ctx: LogContext,\n usage: {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n cost: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };\n },\n contextTokens?: number,\n contextWindow?: number,\n): string {\n const formatTokens = (count: number): string => {\n if (count < 1000) return count.toString();\n if (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n if (count < 1000000) return `${Math.round(count / 1000)}k`;\n return `${(count / 1000000).toFixed(1)}M`;\n };\n\n const lines: string[] = [];\n lines.push(\"_Usage Summary_\");\n lines.push(`Tokens: ${usage.input.toLocaleString()} in, ${usage.output.toLocaleString()} out`);\n if (usage.cacheRead > 0 || usage.cacheWrite > 0) {\n lines.push(\n `Cache: ${usage.cacheRead.toLocaleString()} read, ${usage.cacheWrite.toLocaleString()} write`,\n );\n }\n if (contextTokens && contextWindow) {\n const contextPercent = ((contextTokens / contextWindow) * 100).toFixed(1);\n lines.push(\n `Context: ${formatTokens(contextTokens)} / ${formatTokens(contextWindow)} (${contextPercent}%)`,\n );\n }\n lines.push(\n `Cost: $${usage.cost.input.toFixed(4)} in, $${usage.cost.output.toFixed(4)} out` +\n (usage.cacheRead > 0 || usage.cacheWrite > 0\n ? `, $${usage.cost.cacheRead.toFixed(4)} cache read, $${usage.cost.cacheWrite.toFixed(4)} cache write`\n : \"\"),\n );\n lines.push(`*Total: $${usage.cost.total.toFixed(4)}*`);\n\n const summary = lines.join(\"\\n\");\n\n // Log to console\n if (logger) {\n logger.info(\n {\n event: \"usage\",\n ...ctxFields(ctx),\n tokensIn: usage.input,\n tokensOut: usage.output,\n cacheRead: usage.cacheRead,\n cacheWrite: usage.cacheWrite,\n cost: usage.cost.total,\n },\n `Usage: $${usage.cost.total.toFixed(4)}`,\n );\n }\n console.log(chalk.yellow(`${timestamp()} ${formatContext(ctx)} 💰 Usage`));\n console.log(\n chalk.dim(\n ` ${usage.input.toLocaleString()} in + ${usage.output.toLocaleString()} out` +\n (usage.cacheRead > 0 || usage.cacheWrite > 0\n ? ` (${usage.cacheRead.toLocaleString()} cache read, ${usage.cacheWrite.toLocaleString()} cache write)`\n : \"\") +\n ` = $${usage.cost.total.toFixed(4)}`,\n ),\n );\n\n return summary;\n}\n\n// Startup (no context needed)\nexport function logStartup(workingDir: string, sandbox: string): void {\n if (logger) logger.info({ event: \"startup\", workingDir, sandbox }, \"Starting mama\");\n console.log(\"Starting mama...\");\n console.log(` Working directory: ${workingDir}`);\n console.log(` Sandbox: ${sandbox}`);\n}\n\nexport function logConnected(): void {\n if (logger) logger.info({ event: \"connected\" }, \"Mama connected and listening\");\n console.log(\"⚡️ Mama connected and listening!\");\n console.log(\"\");\n}\n\nexport function logDisconnected(): void {\n if (logger) logger.info({ event: \"disconnected\" }, \"Mama disconnected\");\n console.log(\"Mama disconnected.\");\n}\n\n// Backfill\nexport function logBackfillStart(channelCount: number): void {\n if (logger)\n logger.info({ event: \"backfill_start\", channelCount }, `Backfilling ${channelCount} channels`);\n console.log(chalk.blue(`${timestamp()} [system] Backfilling ${channelCount} channels...`));\n}\n\nexport function logBackfillChannel(channelName: string, messageCount: number): void {\n if (logger)\n logger.debug(\n { event: \"backfill_channel\", channelName, messageCount },\n `#${channelName}: ${messageCount} messages`,\n );\n console.log(chalk.blue(`${timestamp()} [system] #${channelName}: ${messageCount} messages`));\n}\n\nexport function logBackfillComplete(totalMessages: number, durationMs: number): void {\n if (logger)\n logger.info(\n { event: \"backfill_complete\", totalMessages, durationMs },\n `Backfill complete: ${totalMessages} messages`,\n );\n const duration = (durationMs / 1000).toFixed(1);\n console.log(\n chalk.blue(\n `${timestamp()} [system] Backfill complete: ${totalMessages} messages in ${duration}s`,\n ),\n );\n}\n"]}
|
package/dist/main.d.ts
CHANGED
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\n\nimport { join, resolve } from \"path\";\nimport { readFileSync } from \"fs\";\nimport { fileURLToPath } from \"url\";\nimport { dirname, join as pathJoin } from \"path\";\nimport type { Bot, BotAdapters, BotEvent, BotHandler } from \"./adapter.js\";\nimport { DiscordBot } from \"./adapters/discord/index.js\";\nimport { TelegramBot } from \"./adapters/telegram/index.js\";\nimport { SlackBot as SlackBotClass } from \"./adapters/slack/index.js\";\nimport { type AgentRunner, createRunner } from \"./agent.js\";\nimport { downloadChannel } from \"./download.js\";\nimport { createEventsWatcher } from \"./events.js\";\nimport * as log from \"./log.js\";\nimport { parseSandboxArg, type SandboxConfig, validateSandbox } from \"./sandbox.js\";\nimport { ChannelStore } from \"./store.js\";\n\n// ============================================================================\n// Config\n// ============================================================================\n\n// Get version from package.json\nfunction getVersion(): string {\n // Try to find package.json in the dist directory or parent\n const possiblePaths = [\n pathJoin(dirname(fileURLToPath(import.meta.url)), \"package.json\"),\n pathJoin(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\"),\n pathJoin(process.cwd(), \"package.json\"),\n ];\n\n for (const pkgPath of possiblePaths) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.version) return pkg.version;\n } catch {\n // Continue to next path\n }\n }\n return \"unknown\";\n}\n\nconst MOM_SLACK_APP_TOKEN = process.env.MOM_SLACK_APP_TOKEN;\nconst MOM_SLACK_BOT_TOKEN = process.env.MOM_SLACK_BOT_TOKEN;\nconst MOM_TELEGRAM_BOT_TOKEN = process.env.MOM_TELEGRAM_BOT_TOKEN;\nconst MOM_DISCORD_BOT_TOKEN = process.env.MOM_DISCORD_BOT_TOKEN;\n\ninterface ParsedArgs {\n workingDir?: string;\n sandbox: SandboxConfig;\n downloadChannel?: string;\n showVersion?: boolean;\n}\n\nfunction parseArgs(): ParsedArgs {\n const args = process.argv.slice(2);\n let sandbox: SandboxConfig = { type: \"host\" };\n let workingDir: string | undefined;\n let downloadChannelId: string | undefined;\n let showVersion = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--version\" || arg === \"-v\" || arg === \"-V\") {\n showVersion = true;\n } else if (arg.startsWith(\"--sandbox=\")) {\n sandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n } else if (arg === \"--sandbox\") {\n sandbox = parseSandboxArg(args[++i] || \"\");\n } else if (arg.startsWith(\"--download=\")) {\n downloadChannelId = arg.slice(\"--download=\".length);\n } else if (arg === \"--download\") {\n downloadChannelId = args[++i];\n } else if (!arg.startsWith(\"-\")) {\n workingDir = arg;\n }\n }\n\n return {\n workingDir: workingDir ? resolve(workingDir) : undefined,\n sandbox,\n downloadChannel: downloadChannelId,\n showVersion,\n };\n}\n\nconst parsedArgs = parseArgs();\n\n// Handle --version\nif (parsedArgs.showVersion) {\n console.log(getVersion());\n process.exit(0);\n}\n\n// Handle --download mode (Slack only)\nif (parsedArgs.downloadChannel) {\n if (!MOM_SLACK_BOT_TOKEN) {\n console.error(\"Missing env: MOM_SLACK_BOT_TOKEN\");\n process.exit(1);\n }\n await downloadChannel(parsedArgs.downloadChannel, MOM_SLACK_BOT_TOKEN);\n process.exit(0);\n}\n\n// Normal bot mode - require working dir\nif (!parsedArgs.workingDir) {\n console.error(\"Usage: mama [--sandbox=host|docker:<name>] <working-directory>\");\n console.error(\" mama --download <channel-id>\");\n process.exit(1);\n}\n\nconst { workingDir, sandbox } = { workingDir: parsedArgs.workingDir, sandbox: parsedArgs.sandbox };\n\n// Validate platform tokens\nconst hasSlack = !!(MOM_SLACK_APP_TOKEN && MOM_SLACK_BOT_TOKEN);\nconst hasTelegram = !!MOM_TELEGRAM_BOT_TOKEN;\nconst hasDiscord = !!MOM_DISCORD_BOT_TOKEN;\n\nif (!hasSlack && !hasTelegram && !hasDiscord) {\n console.error(\n \"No platform tokens found. Set one of:\\n\" +\n \" Slack: MOM_SLACK_APP_TOKEN + MOM_SLACK_BOT_TOKEN\\n\" +\n \" Telegram: MOM_TELEGRAM_BOT_TOKEN\\n\" +\n \" Discord: MOM_DISCORD_BOT_TOKEN\",\n );\n process.exit(1);\n}\n\nawait validateSandbox(sandbox);\n\n// ============================================================================\n// State (per channel)\n// ============================================================================\n\ninterface ChannelState {\n running: boolean;\n runner: AgentRunner;\n stopRequested: boolean;\n stopMessageTs?: string;\n lastAccessedAt: number;\n startedAt?: number;\n lastActivityAt?: number;\n}\n\nconst channelStates = new Map<string, ChannelState>();\n\n/** Track in-flight runs for graceful shutdown */\nconst inFlightRuns = new Set<Promise<void>>();\n\n/** Flag to stop accepting new events during shutdown */\nlet isShuttingDown = false;\n\n/** Maximum number of cached sessions */\nconst MAX_SESSIONS = 500;\n/** Idle timeout before a non-running session can be evicted (1 hour) */\nconst IDLE_TIMEOUT_MS = 3600000;\n\nasync function getState(channelId: string, sessionKey?: string): Promise<ChannelState> {\n const key = sessionKey ?? channelId;\n let state = channelStates.get(key);\n if (!state) {\n const channelDir = join(workingDir, channelId);\n state = {\n running: false,\n runner: await createRunner(sandbox, key, channelId, channelDir, workingDir),\n stopRequested: false,\n lastAccessedAt: Date.now(),\n };\n channelStates.set(key, state);\n } else {\n state.lastAccessedAt = Date.now();\n }\n return state;\n}\n\n/**\n * Evict idle sessions from channelStates to bound memory usage.\n * Called after each handleEvent completes.\n */\nfunction evictIdleSessions(): void {\n const now = Date.now();\n\n for (const [key, state] of channelStates) {\n if (!state.running && now - state.lastAccessedAt > IDLE_TIMEOUT_MS) {\n channelStates.delete(key);\n }\n }\n\n if (channelStates.size > MAX_SESSIONS) {\n const idleSessions: Array<{ key: string; lastAccessedAt: number }> = [];\n for (const [key, state] of channelStates) {\n if (!state.running) {\n idleSessions.push({ key, lastAccessedAt: state.lastAccessedAt });\n }\n }\n\n idleSessions.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);\n\n const toEvict = channelStates.size - MAX_SESSIONS;\n for (let i = 0; i < toEvict && i < idleSessions.length; i++) {\n channelStates.delete(idleSessions[i].key);\n }\n }\n}\n\n// ============================================================================\n// Handler\n// ============================================================================\n\nconst handler: BotHandler = {\n isRunning(sessionKey: string): boolean {\n const state = channelStates.get(sessionKey);\n return state?.running ?? false;\n },\n\n getRunningSessions() {\n const sessions: import(\"./adapter.js\").RunningSession[] = [];\n for (const [sessionKey, state] of channelStates) {\n if (state.running && state.startedAt) {\n // Get current step from runner\n const currentStep = state.runner.getCurrentStep();\n sessions.push({\n sessionKey,\n startedAt: state.startedAt,\n lastActivityAt: state.lastActivityAt,\n currentTool: currentStep?.label || currentStep?.toolName,\n });\n }\n }\n return sessions;\n },\n\n async handleStop(sessionKey: string, channelId: string, bot: Bot): Promise<void> {\n const state = channelStates.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n const ts = await bot.postMessage(channelId, \"_Stopping..._\");\n state.stopMessageTs = ts;\n } else {\n await bot.postMessage(channelId, \"_Nothing running_\");\n }\n },\n\n forceStop(sessionKey: string): void {\n const state = channelStates.get(sessionKey);\n if (state?.running) {\n log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);\n state.stopRequested = true;\n state.runner.abort();\n // Force set running to false immediately\n state.running = false;\n }\n },\n\n async handleEvent(\n event: BotEvent,\n bot: Bot,\n adapters: BotAdapters,\n _isEvent?: boolean,\n ): Promise<void> {\n // Don't accept new events during shutdown\n if (isShuttingDown) {\n log.logInfo(\n `[${event.channel}] Rejected event during shutdown: ${event.text.substring(0, 50)}`,\n );\n return;\n }\n\n const sessionKey = `${event.channel}:${event.thread_ts ?? event.ts}`;\n const state = await getState(event.channel, sessionKey);\n\n // Start run\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n log.logInfo(`[${event.channel}] Starting run: ${event.text.substring(0, 50)}`);\n\n // Wrap in-flight run tracking\n const runPromise = (async () => {\n try {\n const { message, responseCtx, platform } = adapters;\n\n // Run the agent\n await responseCtx.setTyping(true);\n await responseCtx.setWorking(true);\n const result = await state.runner.run(message, responseCtx, platform);\n await responseCtx.setWorking(false);\n\n if (result.stopReason === \"aborted\" && state.stopRequested) {\n if (state.stopMessageTs) {\n await bot.updateMessage(event.channel, state.stopMessageTs, \"_Stopped_\");\n state.stopMessageTs = undefined;\n } else {\n await bot.postMessage(event.channel, \"_Stopped_\");\n }\n }\n } catch (err) {\n log.logWarning(\n `[${event.channel}] Run error`,\n err instanceof Error ? err.message : String(err),\n );\n } finally {\n state.running = false;\n state.lastAccessedAt = Date.now();\n evictIdleSessions();\n }\n })();\n\n inFlightRuns.add(runPromise);\n try {\n await runPromise;\n } finally {\n inFlightRuns.delete(runPromise);\n }\n },\n};\n\n// ============================================================================\n// Start\n// ============================================================================\n\nlog.logStartup(workingDir, sandbox.type === \"host\" ? \"host\" : `docker:${sandbox.container}`);\n\n// Create the appropriate platform bot\nlet bot: Bot;\n\nif (hasSlack) {\n const sharedStore = new ChannelStore({ workingDir, botToken: MOM_SLACK_BOT_TOKEN! });\n bot = new SlackBotClass(handler, {\n appToken: MOM_SLACK_APP_TOKEN!,\n botToken: MOM_SLACK_BOT_TOKEN!,\n workingDir,\n store: sharedStore,\n });\n log.logInfo(\"Platform: Slack\");\n} else if (hasTelegram) {\n bot = new TelegramBot(handler, {\n token: MOM_TELEGRAM_BOT_TOKEN!,\n workingDir,\n });\n log.logInfo(\"Platform: Telegram\");\n} else {\n bot = new DiscordBot(handler, {\n token: MOM_DISCORD_BOT_TOKEN!,\n workingDir,\n });\n log.logInfo(\"Platform: Discord\");\n}\n\n// Start events watcher\nconst eventsWatcher = createEventsWatcher(workingDir, bot);\nif (hasSlack) {\n (bot as SlackBotClass).setEventsWatcher(eventsWatcher);\n}\neventsWatcher.start();\n\n// Handle shutdown\nprocess.on(\"SIGINT\", async () => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n log.logInfo(\"Shutting down gracefully...\");\n\n const timeout = Date.now() + 30000;\n while (inFlightRuns.size > 0 && Date.now() < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n\n if (inFlightRuns.size > 0) {\n log.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n }\n\n eventsWatcher.stop();\n process.exit(0);\n});\n\nprocess.on(\"SIGTERM\", async () => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n log.logInfo(\"Shutting down gracefully...\");\n\n const timeout = Date.now() + 30000;\n while (inFlightRuns.size > 0 && Date.now() < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n\n if (inFlightRuns.size > 0) {\n log.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n }\n\n eventsWatcher.stop();\n process.exit(0);\n});\n\nbot.start().catch((err) => {\n log.logWarning(\"Failed to start bot\", err instanceof Error ? err.message : String(err));\n process.exit(1);\n});\n"]}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAEA,OAAO,iBAAiB,CAAC","sourcesContent":["#!/usr/bin/env node\n\nimport \"./instrument.js\";\n\nimport { join, resolve } from \"path\";\nimport { readFileSync } from \"fs\";\nimport { fileURLToPath } from \"url\";\nimport { dirname, join as pathJoin } from \"path\";\nimport type { Bot, BotAdapters, BotEvent, BotHandler } from \"./adapter.js\";\nimport { DiscordBot } from \"./adapters/discord/index.js\";\nimport { TelegramBot } from \"./adapters/telegram/index.js\";\nimport { SlackBot as SlackBotClass } from \"./adapters/slack/index.js\";\nimport { type AgentRunner, createRunner } from \"./agent.js\";\nimport {\n createManagedSessionFile,\n createManagedSessionFileAtPath,\n getSessionDir,\n getThreadSessionFile,\n} from \"./session-store.js\";\nimport { downloadChannel } from \"./download.js\";\nimport { createEventsWatcher } from \"./events.js\";\nimport * as log from \"./log.js\";\nimport { parseSandboxArg, type SandboxConfig, validateSandbox } from \"./sandbox.js\";\nimport { addLifecycleBreadcrumb, applyRunScope } from \"./sentry.js\";\nimport { ChannelStore } from \"./store.js\";\nimport * as Sentry from \"@sentry/node\";\n\n// ============================================================================\n// Config\n// ============================================================================\n\n// Get version from package.json\nfunction getVersion(): string {\n // Try to find package.json in the dist directory or parent\n const possiblePaths = [\n pathJoin(dirname(fileURLToPath(import.meta.url)), \"package.json\"),\n pathJoin(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\"),\n pathJoin(process.cwd(), \"package.json\"),\n ];\n\n for (const pkgPath of possiblePaths) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.version) return pkg.version;\n } catch {\n // Continue to next path\n }\n }\n return \"unknown\";\n}\n\nconst MOM_SLACK_APP_TOKEN = process.env.MOM_SLACK_APP_TOKEN;\nconst MOM_SLACK_BOT_TOKEN = process.env.MOM_SLACK_BOT_TOKEN;\nconst MOM_TELEGRAM_BOT_TOKEN = process.env.MOM_TELEGRAM_BOT_TOKEN;\nconst MOM_DISCORD_BOT_TOKEN = process.env.MOM_DISCORD_BOT_TOKEN;\n\ninterface ParsedArgs {\n workingDir?: string;\n sandbox: SandboxConfig;\n downloadChannel?: string;\n showVersion?: boolean;\n}\n\nfunction parseArgs(): ParsedArgs {\n const args = process.argv.slice(2);\n let sandbox: SandboxConfig = { type: \"host\" };\n let workingDir: string | undefined;\n let downloadChannelId: string | undefined;\n let showVersion = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--version\" || arg === \"-v\" || arg === \"-V\") {\n showVersion = true;\n } else if (arg.startsWith(\"--sandbox=\")) {\n sandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n } else if (arg === \"--sandbox\") {\n sandbox = parseSandboxArg(args[++i] || \"\");\n } else if (arg.startsWith(\"--download=\")) {\n downloadChannelId = arg.slice(\"--download=\".length);\n } else if (arg === \"--download\") {\n downloadChannelId = args[++i];\n } else if (!arg.startsWith(\"-\")) {\n workingDir = arg;\n }\n }\n\n return {\n workingDir: workingDir ? resolve(workingDir) : undefined,\n sandbox,\n downloadChannel: downloadChannelId,\n showVersion,\n };\n}\n\nconst parsedArgs = parseArgs();\n\n// Handle --version\nif (parsedArgs.showVersion) {\n console.log(getVersion());\n process.exit(0);\n}\n\n// Handle --download mode (Slack only)\nif (parsedArgs.downloadChannel) {\n if (!MOM_SLACK_BOT_TOKEN) {\n console.error(\"Missing env: MOM_SLACK_BOT_TOKEN\");\n process.exit(1);\n }\n await downloadChannel(parsedArgs.downloadChannel, MOM_SLACK_BOT_TOKEN);\n process.exit(0);\n}\n\n// Normal bot mode - require working dir\nif (!parsedArgs.workingDir) {\n console.error(\n \"Usage: mama [--sandbox=host|docker:<name>|firecracker:<vm-id>:<host-path>] <working-directory>\",\n );\n console.error(\" mama --download <channel-id>\");\n process.exit(1);\n}\n\nconst { workingDir, sandbox } = { workingDir: parsedArgs.workingDir, sandbox: parsedArgs.sandbox };\n\n// Validate platform tokens\nconst hasSlack = !!(MOM_SLACK_APP_TOKEN && MOM_SLACK_BOT_TOKEN);\nconst hasTelegram = !!MOM_TELEGRAM_BOT_TOKEN;\nconst hasDiscord = !!MOM_DISCORD_BOT_TOKEN;\n\nif (!hasSlack && !hasTelegram && !hasDiscord) {\n console.error(\n \"No platform tokens found. Set one of:\\n\" +\n \" Slack: MOM_SLACK_APP_TOKEN + MOM_SLACK_BOT_TOKEN\\n\" +\n \" Telegram: MOM_TELEGRAM_BOT_TOKEN\\n\" +\n \" Discord: MOM_DISCORD_BOT_TOKEN\",\n );\n process.exit(1);\n}\n\nawait validateSandbox(sandbox);\n\n// ============================================================================\n// State (per channel)\n// ============================================================================\n\ninterface ChannelState {\n running: boolean;\n runner: AgentRunner;\n stopRequested: boolean;\n stopMessageTs?: string;\n lastAccessedAt: number;\n startedAt?: number;\n lastActivityAt?: number;\n}\n\nconst channelStates = new Map<string, ChannelState>();\n\n/** Track in-flight runs for graceful shutdown */\nconst inFlightRuns = new Set<Promise<void>>();\n\n/** Flag to stop accepting new events during shutdown */\nlet isShuttingDown = false;\n\n/** Maximum number of cached sessions */\nconst MAX_SESSIONS = 500;\n/** Idle timeout before a non-running session can be evicted (1 hour) */\nconst IDLE_TIMEOUT_MS = 3600000;\n\nasync function getState(channelId: string, sessionKey?: string): Promise<ChannelState> {\n const key = sessionKey ?? channelId;\n let state = channelStates.get(key);\n if (!state) {\n const channelDir = join(workingDir, channelId);\n state = {\n running: false,\n runner: await createRunner(sandbox, key, channelId, channelDir, workingDir),\n stopRequested: false,\n lastAccessedAt: Date.now(),\n };\n channelStates.set(key, state);\n } else {\n state.lastAccessedAt = Date.now();\n }\n return state;\n}\n\n/**\n * Evict idle sessions from channelStates to bound memory usage.\n * Called after each handleEvent completes.\n */\nfunction evictIdleSessions(): void {\n const now = Date.now();\n\n for (const [key, state] of channelStates) {\n if (!state.running && now - state.lastAccessedAt > IDLE_TIMEOUT_MS) {\n channelStates.delete(key);\n }\n }\n\n if (channelStates.size > MAX_SESSIONS) {\n const idleSessions: Array<{ key: string; lastAccessedAt: number }> = [];\n for (const [key, state] of channelStates) {\n if (!state.running) {\n idleSessions.push({ key, lastAccessedAt: state.lastAccessedAt });\n }\n }\n\n idleSessions.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);\n\n const toEvict = channelStates.size - MAX_SESSIONS;\n for (let i = 0; i < toEvict && i < idleSessions.length; i++) {\n channelStates.delete(idleSessions[i].key);\n }\n }\n}\n\n// ============================================================================\n// Handler\n// ============================================================================\n\nconst handler: BotHandler = {\n isRunning(sessionKey: string): boolean {\n const state = channelStates.get(sessionKey);\n return !!state?.running;\n },\n\n getRunningSessions() {\n const sessions: import(\"./adapter.js\").RunningSession[] = [];\n for (const [sessionKey, state] of channelStates) {\n if (state.running && state.startedAt) {\n // Get current step from runner\n const currentStep = state.runner.getCurrentStep();\n sessions.push({\n sessionKey,\n startedAt: state.startedAt,\n lastActivityAt: state.lastActivityAt,\n currentTool: currentStep?.label || currentStep?.toolName,\n });\n }\n }\n return sessions;\n },\n\n async handleStop(sessionKey: string, channelId: string, bot: Bot): Promise<void> {\n const state = channelStates.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n const ts = await bot.postMessage(channelId, \"_Stopping..._\");\n state.stopMessageTs = ts;\n } else {\n await bot.postMessage(channelId, \"_Nothing running_\");\n }\n },\n\n forceStop(sessionKey: string): void {\n const state = channelStates.get(sessionKey);\n if (state?.running) {\n log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);\n state.stopRequested = true;\n state.runner.abort();\n state.running = false;\n }\n },\n\n async handleNew(sessionKey: string, channelId: string, bot: Bot): Promise<void> {\n const state = channelStates.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n }\n\n // Channel sessions rotate via current pointer. Thread sessions reset in place.\n const channelDir = join(workingDir, channelId);\n if (sessionKey.includes(\":\")) {\n createManagedSessionFileAtPath(getThreadSessionFile(channelDir, sessionKey), channelDir);\n } else {\n createManagedSessionFile(getSessionDir(channelDir, sessionKey), channelDir);\n }\n\n // Remove from in-memory cache\n channelStates.delete(sessionKey);\n\n log.logInfo(`[${channelId}] Session reset: ${sessionKey}`);\n await bot.postMessage(channelId, \"Conversation reset. Send a new message to start fresh.\");\n },\n\n async handleEvent(\n event: BotEvent,\n bot: Bot,\n adapters: BotAdapters,\n _isEvent?: boolean,\n ): Promise<void> {\n // Don't accept new events during shutdown\n if (isShuttingDown) {\n log.logInfo(\n `[${event.channel}] Rejected event during shutdown: ${event.text.substring(0, 50)}`,\n );\n return;\n }\n\n const sessionKey = event.sessionKey ?? `${event.channel}:${event.thread_ts ?? event.ts}`;\n const state = await getState(event.channel, sessionKey);\n\n // Start run\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n log.logInfo(`[${event.channel}] Starting run: ${event.text.substring(0, 50)}`);\n\n // Wrap in-flight run tracking\n Sentry.metrics.count(\"agent.run.started\", 1, {\n attributes: { channel: event.channel },\n });\n Sentry.metrics.gauge(\"agent.sessions.active\", inFlightRuns.size + 1);\n\n const runPromise = Sentry.startSpan(\n { name: \"agent.run\", op: \"agent\", attributes: { channelId: event.channel, sessionKey } },\n async () => {\n return Sentry.withScope(async (scope) => {\n const { message, responseCtx, platform } = adapters;\n applyRunScope(scope, {\n channelId: event.channel,\n sessionKey,\n messageId: message.id,\n platform: platform.name,\n userId: message.userId,\n userName: message.userName,\n threadTs: message.threadTs,\n isEvent: _isEvent,\n });\n addLifecycleBreadcrumb(\"agent.run.started\", {\n channel_id: event.channel,\n platform: platform.name,\n has_attachments: (message.attachments?.length ?? 0) > 0,\n });\n\n try {\n await responseCtx.setTyping(true);\n await responseCtx.setWorking(true);\n const result = await state.runner.run(message, responseCtx, platform);\n await responseCtx.setWorking(false);\n\n const durationMs = Date.now() - state.startedAt!;\n Sentry.metrics.distribution(\"agent.run.duration\", durationMs, {\n unit: \"millisecond\",\n attributes: {\n channel: event.channel,\n platform: platform.name,\n stop_reason: result.stopReason,\n },\n });\n Sentry.metrics.count(\"agent.run.completed\", 1, {\n attributes: {\n channel: event.channel,\n platform: platform.name,\n stop_reason: result.stopReason,\n },\n });\n addLifecycleBreadcrumb(\"agent.run.completed\", {\n channel_id: event.channel,\n platform: platform.name,\n stop_reason: result.stopReason,\n duration_ms: durationMs,\n });\n\n if (result.stopReason === \"aborted\" && state.stopRequested) {\n if (state.stopMessageTs) {\n await bot.updateMessage(event.channel, state.stopMessageTs, \"_Stopped_\");\n state.stopMessageTs = undefined;\n } else {\n await bot.postMessage(event.channel, \"_Stopped_\");\n }\n }\n } catch (err) {\n scope.setContext(\"agent_run_error\", {\n channelId: event.channel,\n sessionKey,\n platform: adapters.platform.name,\n messageId: adapters.message.id,\n threadTs: adapters.message.threadTs,\n });\n Sentry.captureException(err);\n Sentry.metrics.count(\"agent.run.errors\", 1, {\n attributes: { channel: event.channel, platform: adapters.platform.name },\n });\n log.logWarning(\n `[${event.channel}] Run error`,\n err instanceof Error ? err.message : String(err),\n );\n } finally {\n state.running = false;\n state.lastAccessedAt = Date.now();\n Sentry.metrics.gauge(\"agent.sessions.active\", inFlightRuns.size - 1);\n evictIdleSessions();\n }\n });\n },\n );\n\n inFlightRuns.add(runPromise);\n try {\n await runPromise;\n } finally {\n inFlightRuns.delete(runPromise);\n }\n },\n};\n\n// ============================================================================\n// Start\n// ============================================================================\n\nconst sandboxDesc =\n sandbox.type === \"host\"\n ? \"host\"\n : sandbox.type === \"docker\"\n ? `docker:${sandbox.container}`\n : `firecracker:${sandbox.vmId}`;\nlog.logStartup(workingDir, sandboxDesc);\n\n// Create platform bots\nconst bots: Bot[] = [];\nconst botsByPlatform: Record<string, Bot> = {};\n\nif (hasSlack) {\n const sharedStore = new ChannelStore({ workingDir, botToken: MOM_SLACK_BOT_TOKEN! });\n const slackBot = new SlackBotClass(handler, {\n appToken: MOM_SLACK_APP_TOKEN!,\n botToken: MOM_SLACK_BOT_TOKEN!,\n workingDir,\n store: sharedStore,\n });\n bots.push(slackBot);\n botsByPlatform.slack = slackBot;\n log.logInfo(\"Platform: Slack\");\n}\nif (hasTelegram) {\n const telegramBot = new TelegramBot(handler, {\n token: MOM_TELEGRAM_BOT_TOKEN!,\n workingDir,\n });\n bots.push(telegramBot);\n botsByPlatform.telegram = telegramBot;\n log.logInfo(\"Platform: Telegram\");\n}\nif (hasDiscord) {\n const discordBot = new DiscordBot(handler, {\n token: MOM_DISCORD_BOT_TOKEN!,\n workingDir,\n });\n bots.push(discordBot);\n botsByPlatform.discord = discordBot;\n log.logInfo(\"Platform: Discord\");\n}\n\n// Start events watcher with explicit platform routing\nconst eventsWatcher = createEventsWatcher(workingDir, botsByPlatform);\nconst slackBot = botsByPlatform.slack as SlackBotClass | undefined;\nif (slackBot) {\n slackBot.setEventsWatcher(eventsWatcher);\n}\neventsWatcher.start();\n\n// Handle shutdown\nasync function shutdown(): Promise<void> {\n if (isShuttingDown) return;\n isShuttingDown = true;\n log.logInfo(\"Shutting down gracefully...\");\n\n const timeout = Date.now() + 30000;\n while (inFlightRuns.size > 0 && Date.now() < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n\n if (inFlightRuns.size > 0) {\n log.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n }\n\n eventsWatcher.stop();\n await Sentry.close(5000);\n process.exit(0);\n}\n\nprocess.on(\"SIGINT\", shutdown);\nprocess.on(\"SIGTERM\", shutdown);\n\n// Start all bots\nawait Promise.all(\n bots.map((bot) =>\n bot.start().catch((err) => {\n log.logWarning(\"Failed to start bot\", err instanceof Error ? err.message : String(err));\n process.exit(1);\n }),\n ),\n);\n"]}
|
package/dist/main.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "./instrument.js";
|
|
2
3
|
import { join, resolve } from "path";
|
|
3
4
|
import { readFileSync } from "fs";
|
|
4
5
|
import { fileURLToPath } from "url";
|
|
@@ -7,11 +8,14 @@ import { DiscordBot } from "./adapters/discord/index.js";
|
|
|
7
8
|
import { TelegramBot } from "./adapters/telegram/index.js";
|
|
8
9
|
import { SlackBot as SlackBotClass } from "./adapters/slack/index.js";
|
|
9
10
|
import { createRunner } from "./agent.js";
|
|
11
|
+
import { createManagedSessionFile, createManagedSessionFileAtPath, getSessionDir, getThreadSessionFile, } from "./session-store.js";
|
|
10
12
|
import { downloadChannel } from "./download.js";
|
|
11
13
|
import { createEventsWatcher } from "./events.js";
|
|
12
14
|
import * as log from "./log.js";
|
|
13
15
|
import { parseSandboxArg, validateSandbox } from "./sandbox.js";
|
|
16
|
+
import { addLifecycleBreadcrumb, applyRunScope } from "./sentry.js";
|
|
14
17
|
import { ChannelStore } from "./store.js";
|
|
18
|
+
import * as Sentry from "@sentry/node";
|
|
15
19
|
// ============================================================================
|
|
16
20
|
// Config
|
|
17
21
|
// ============================================================================
|
|
@@ -90,7 +94,7 @@ if (parsedArgs.downloadChannel) {
|
|
|
90
94
|
}
|
|
91
95
|
// Normal bot mode - require working dir
|
|
92
96
|
if (!parsedArgs.workingDir) {
|
|
93
|
-
console.error("Usage: mama [--sandbox=host|docker:<name>] <working-directory>");
|
|
97
|
+
console.error("Usage: mama [--sandbox=host|docker:<name>|firecracker:<vm-id>:<host-path>] <working-directory>");
|
|
94
98
|
console.error(" mama --download <channel-id>");
|
|
95
99
|
process.exit(1);
|
|
96
100
|
}
|
|
@@ -165,7 +169,7 @@ function evictIdleSessions() {
|
|
|
165
169
|
const handler = {
|
|
166
170
|
isRunning(sessionKey) {
|
|
167
171
|
const state = channelStates.get(sessionKey);
|
|
168
|
-
return state?.running
|
|
172
|
+
return !!state?.running;
|
|
169
173
|
},
|
|
170
174
|
getRunningSessions() {
|
|
171
175
|
const sessions = [];
|
|
@@ -201,17 +205,35 @@ const handler = {
|
|
|
201
205
|
log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);
|
|
202
206
|
state.stopRequested = true;
|
|
203
207
|
state.runner.abort();
|
|
204
|
-
// Force set running to false immediately
|
|
205
208
|
state.running = false;
|
|
206
209
|
}
|
|
207
210
|
},
|
|
211
|
+
async handleNew(sessionKey, channelId, bot) {
|
|
212
|
+
const state = channelStates.get(sessionKey);
|
|
213
|
+
if (state?.running) {
|
|
214
|
+
state.stopRequested = true;
|
|
215
|
+
state.runner.abort();
|
|
216
|
+
}
|
|
217
|
+
// Channel sessions rotate via current pointer. Thread sessions reset in place.
|
|
218
|
+
const channelDir = join(workingDir, channelId);
|
|
219
|
+
if (sessionKey.includes(":")) {
|
|
220
|
+
createManagedSessionFileAtPath(getThreadSessionFile(channelDir, sessionKey), channelDir);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
createManagedSessionFile(getSessionDir(channelDir, sessionKey), channelDir);
|
|
224
|
+
}
|
|
225
|
+
// Remove from in-memory cache
|
|
226
|
+
channelStates.delete(sessionKey);
|
|
227
|
+
log.logInfo(`[${channelId}] Session reset: ${sessionKey}`);
|
|
228
|
+
await bot.postMessage(channelId, "Conversation reset. Send a new message to start fresh.");
|
|
229
|
+
},
|
|
208
230
|
async handleEvent(event, bot, adapters, _isEvent) {
|
|
209
231
|
// Don't accept new events during shutdown
|
|
210
232
|
if (isShuttingDown) {
|
|
211
233
|
log.logInfo(`[${event.channel}] Rejected event during shutdown: ${event.text.substring(0, 50)}`);
|
|
212
234
|
return;
|
|
213
235
|
}
|
|
214
|
-
const sessionKey = `${event.channel}:${event.thread_ts ?? event.ts}`;
|
|
236
|
+
const sessionKey = event.sessionKey ?? `${event.channel}:${event.thread_ts ?? event.ts}`;
|
|
215
237
|
const state = await getState(event.channel, sessionKey);
|
|
216
238
|
// Start run
|
|
217
239
|
state.running = true;
|
|
@@ -220,33 +242,87 @@ const handler = {
|
|
|
220
242
|
state.lastActivityAt = Date.now();
|
|
221
243
|
log.logInfo(`[${event.channel}] Starting run: ${event.text.substring(0, 50)}`);
|
|
222
244
|
// Wrap in-flight run tracking
|
|
223
|
-
|
|
224
|
-
|
|
245
|
+
Sentry.metrics.count("agent.run.started", 1, {
|
|
246
|
+
attributes: { channel: event.channel },
|
|
247
|
+
});
|
|
248
|
+
Sentry.metrics.gauge("agent.sessions.active", inFlightRuns.size + 1);
|
|
249
|
+
const runPromise = Sentry.startSpan({ name: "agent.run", op: "agent", attributes: { channelId: event.channel, sessionKey } }, async () => {
|
|
250
|
+
return Sentry.withScope(async (scope) => {
|
|
225
251
|
const { message, responseCtx, platform } = adapters;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
252
|
+
applyRunScope(scope, {
|
|
253
|
+
channelId: event.channel,
|
|
254
|
+
sessionKey,
|
|
255
|
+
messageId: message.id,
|
|
256
|
+
platform: platform.name,
|
|
257
|
+
userId: message.userId,
|
|
258
|
+
userName: message.userName,
|
|
259
|
+
threadTs: message.threadTs,
|
|
260
|
+
isEvent: _isEvent,
|
|
261
|
+
});
|
|
262
|
+
addLifecycleBreadcrumb("agent.run.started", {
|
|
263
|
+
channel_id: event.channel,
|
|
264
|
+
platform: platform.name,
|
|
265
|
+
has_attachments: (message.attachments?.length ?? 0) > 0,
|
|
266
|
+
});
|
|
267
|
+
try {
|
|
268
|
+
await responseCtx.setTyping(true);
|
|
269
|
+
await responseCtx.setWorking(true);
|
|
270
|
+
const result = await state.runner.run(message, responseCtx, platform);
|
|
271
|
+
await responseCtx.setWorking(false);
|
|
272
|
+
const durationMs = Date.now() - state.startedAt;
|
|
273
|
+
Sentry.metrics.distribution("agent.run.duration", durationMs, {
|
|
274
|
+
unit: "millisecond",
|
|
275
|
+
attributes: {
|
|
276
|
+
channel: event.channel,
|
|
277
|
+
platform: platform.name,
|
|
278
|
+
stop_reason: result.stopReason,
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
Sentry.metrics.count("agent.run.completed", 1, {
|
|
282
|
+
attributes: {
|
|
283
|
+
channel: event.channel,
|
|
284
|
+
platform: platform.name,
|
|
285
|
+
stop_reason: result.stopReason,
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
addLifecycleBreadcrumb("agent.run.completed", {
|
|
289
|
+
channel_id: event.channel,
|
|
290
|
+
platform: platform.name,
|
|
291
|
+
stop_reason: result.stopReason,
|
|
292
|
+
duration_ms: durationMs,
|
|
293
|
+
});
|
|
294
|
+
if (result.stopReason === "aborted" && state.stopRequested) {
|
|
295
|
+
if (state.stopMessageTs) {
|
|
296
|
+
await bot.updateMessage(event.channel, state.stopMessageTs, "_Stopped_");
|
|
297
|
+
state.stopMessageTs = undefined;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
await bot.postMessage(event.channel, "_Stopped_");
|
|
301
|
+
}
|
|
238
302
|
}
|
|
239
303
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
304
|
+
catch (err) {
|
|
305
|
+
scope.setContext("agent_run_error", {
|
|
306
|
+
channelId: event.channel,
|
|
307
|
+
sessionKey,
|
|
308
|
+
platform: adapters.platform.name,
|
|
309
|
+
messageId: adapters.message.id,
|
|
310
|
+
threadTs: adapters.message.threadTs,
|
|
311
|
+
});
|
|
312
|
+
Sentry.captureException(err);
|
|
313
|
+
Sentry.metrics.count("agent.run.errors", 1, {
|
|
314
|
+
attributes: { channel: event.channel, platform: adapters.platform.name },
|
|
315
|
+
});
|
|
316
|
+
log.logWarning(`[${event.channel}] Run error`, err instanceof Error ? err.message : String(err));
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
state.running = false;
|
|
320
|
+
state.lastAccessedAt = Date.now();
|
|
321
|
+
Sentry.metrics.gauge("agent.sessions.active", inFlightRuns.size - 1);
|
|
322
|
+
evictIdleSessions();
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
});
|
|
250
326
|
inFlightRuns.add(runPromise);
|
|
251
327
|
try {
|
|
252
328
|
await runPromise;
|
|
@@ -259,41 +335,54 @@ const handler = {
|
|
|
259
335
|
// ============================================================================
|
|
260
336
|
// Start
|
|
261
337
|
// ============================================================================
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
338
|
+
const sandboxDesc = sandbox.type === "host"
|
|
339
|
+
? "host"
|
|
340
|
+
: sandbox.type === "docker"
|
|
341
|
+
? `docker:${sandbox.container}`
|
|
342
|
+
: `firecracker:${sandbox.vmId}`;
|
|
343
|
+
log.logStartup(workingDir, sandboxDesc);
|
|
344
|
+
// Create platform bots
|
|
345
|
+
const bots = [];
|
|
346
|
+
const botsByPlatform = {};
|
|
265
347
|
if (hasSlack) {
|
|
266
348
|
const sharedStore = new ChannelStore({ workingDir, botToken: MOM_SLACK_BOT_TOKEN });
|
|
267
|
-
|
|
349
|
+
const slackBot = new SlackBotClass(handler, {
|
|
268
350
|
appToken: MOM_SLACK_APP_TOKEN,
|
|
269
351
|
botToken: MOM_SLACK_BOT_TOKEN,
|
|
270
352
|
workingDir,
|
|
271
353
|
store: sharedStore,
|
|
272
354
|
});
|
|
355
|
+
bots.push(slackBot);
|
|
356
|
+
botsByPlatform.slack = slackBot;
|
|
273
357
|
log.logInfo("Platform: Slack");
|
|
274
358
|
}
|
|
275
|
-
|
|
276
|
-
|
|
359
|
+
if (hasTelegram) {
|
|
360
|
+
const telegramBot = new TelegramBot(handler, {
|
|
277
361
|
token: MOM_TELEGRAM_BOT_TOKEN,
|
|
278
362
|
workingDir,
|
|
279
363
|
});
|
|
364
|
+
bots.push(telegramBot);
|
|
365
|
+
botsByPlatform.telegram = telegramBot;
|
|
280
366
|
log.logInfo("Platform: Telegram");
|
|
281
367
|
}
|
|
282
|
-
|
|
283
|
-
|
|
368
|
+
if (hasDiscord) {
|
|
369
|
+
const discordBot = new DiscordBot(handler, {
|
|
284
370
|
token: MOM_DISCORD_BOT_TOKEN,
|
|
285
371
|
workingDir,
|
|
286
372
|
});
|
|
373
|
+
bots.push(discordBot);
|
|
374
|
+
botsByPlatform.discord = discordBot;
|
|
287
375
|
log.logInfo("Platform: Discord");
|
|
288
376
|
}
|
|
289
|
-
// Start events watcher
|
|
290
|
-
const eventsWatcher = createEventsWatcher(workingDir,
|
|
291
|
-
|
|
292
|
-
|
|
377
|
+
// Start events watcher with explicit platform routing
|
|
378
|
+
const eventsWatcher = createEventsWatcher(workingDir, botsByPlatform);
|
|
379
|
+
const slackBot = botsByPlatform.slack;
|
|
380
|
+
if (slackBot) {
|
|
381
|
+
slackBot.setEventsWatcher(eventsWatcher);
|
|
293
382
|
}
|
|
294
383
|
eventsWatcher.start();
|
|
295
384
|
// Handle shutdown
|
|
296
|
-
|
|
385
|
+
async function shutdown() {
|
|
297
386
|
if (isShuttingDown)
|
|
298
387
|
return;
|
|
299
388
|
isShuttingDown = true;
|
|
@@ -306,25 +395,14 @@ process.on("SIGINT", async () => {
|
|
|
306
395
|
log.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);
|
|
307
396
|
}
|
|
308
397
|
eventsWatcher.stop();
|
|
398
|
+
await Sentry.close(5000);
|
|
309
399
|
process.exit(0);
|
|
310
|
-
}
|
|
311
|
-
process.on("
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
log.logInfo("Shutting down gracefully...");
|
|
316
|
-
const timeout = Date.now() + 30000;
|
|
317
|
-
while (inFlightRuns.size > 0 && Date.now() < timeout) {
|
|
318
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
319
|
-
}
|
|
320
|
-
if (inFlightRuns.size > 0) {
|
|
321
|
-
log.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);
|
|
322
|
-
}
|
|
323
|
-
eventsWatcher.stop();
|
|
324
|
-
process.exit(0);
|
|
325
|
-
});
|
|
326
|
-
bot.start().catch((err) => {
|
|
400
|
+
}
|
|
401
|
+
process.on("SIGINT", shutdown);
|
|
402
|
+
process.on("SIGTERM", shutdown);
|
|
403
|
+
// Start all bots
|
|
404
|
+
await Promise.all(bots.map((bot) => bot.start().catch((err) => {
|
|
327
405
|
log.logWarning("Failed to start bot", err instanceof Error ? err.message : String(err));
|
|
328
406
|
process.exit(1);
|
|
329
|
-
});
|
|
407
|
+
})));
|
|
330
408
|
//# sourceMappingURL=main.js.map
|