@geminixiang/mama 0.2.0-beta.19 → 0.2.0-beta.20
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 +11 -17
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +5 -5
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +27 -2
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/shared.d.ts +20 -0
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/shared.js +29 -0
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +3 -3
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +99 -64
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/branch-manager.d.ts +6 -0
- package/dist/adapters/slack/branch-manager.d.ts.map +1 -1
- package/dist/adapters/slack/branch-manager.js +15 -5
- package/dist/adapters/slack/branch-manager.js.map +1 -1
- package/dist/adapters/slack/context.d.ts +4 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +36 -29
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/session.d.ts +35 -0
- package/dist/adapters/slack/session.d.ts.map +1 -1
- package/dist/adapters/slack/session.js +51 -1
- package/dist/adapters/slack/session.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +6 -6
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +27 -6
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +56 -10
- package/dist/agent.js.map +1 -1
- package/dist/commands/auto-reply.d.ts.map +1 -1
- package/dist/commands/auto-reply.js +4 -1
- package/dist/commands/auto-reply.js.map +1 -1
- package/dist/config.d.ts +0 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -39
- package/dist/config.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +37 -2
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +40 -6
- package/dist/execution-resolver.js.map +1 -1
- package/dist/log.d.ts +0 -7
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +0 -106
- package/dist/log.js.map +1 -1
- package/dist/login/portal.d.ts.map +1 -1
- package/dist/login/portal.js +64 -0
- package/dist/login/portal.js.map +1 -1
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +14 -0
- package/dist/provisioner.js.map +1 -1
- package/dist/runtime/conversation-orchestrator.d.ts +1 -2
- package/dist/runtime/conversation-orchestrator.d.ts.map +1 -1
- package/dist/runtime/conversation-orchestrator.js +44 -12
- package/dist/runtime/conversation-orchestrator.js.map +1 -1
- package/dist/runtime/session-runtime.d.ts +0 -1
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/runtime/session-runtime.js +12 -11
- package/dist/runtime/session-runtime.js.map +1 -1
- package/dist/sentry.d.ts +20 -1
- package/dist/sentry.d.ts.map +1 -1
- package/dist/sentry.js +58 -8
- package/dist/sentry.js.map +1 -1
- package/dist/session-view/portal.d.ts.map +1 -1
- package/dist/session-view/portal.js +83 -3
- package/dist/session-view/portal.js.map +1 -1
- package/dist/vault.d.ts.map +1 -1
- package/dist/vault.js +8 -0
- package/dist/vault.js.map +1 -1
- package/package.json +2 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-manager.js","sourceRoot":"","sources":["../../../src/adapters/slack/branch-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createManagedSessionFileAtPath,\n createThreadSessionFileFromRootMessage,\n extractSessionSuffix,\n forkThreadSessionFile,\n forkThreadSessionFileFromRootMessage,\n getChannelSessionDir,\n getThreadSessionFile,\n resolveChannelSessionFile,\n resolveManagedSessionFile,\n type ResolvedSessionScope,\n ThreadRootNotFoundError,\n tryResolveThreadSession,\n type ThreadRootMessage,\n} from \"../../session-store.js\";\nimport { findLogMessageById, type ConversationLogMessage } from \"../../context.js\";\n\nexport interface SlackBranchBootstrapWaitOptions {\n parentSessionKey: string;\n sessionKey: string;\n hasThreadSession: () => boolean;\n isParentRunning: () => boolean;\n sleep?: (ms: number) => Promise<void>;\n pollMs?: number;\n}\n\nexport type SlackResolvedSessionScope = ResolvedSessionScope;\n\nexport interface ResolveSlackSessionScopeOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n sleep?: (ms: number) => Promise<void>;\n retryCount?: number;\n retryDelayMs?: number;\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction buildThreadRootSeed(message: ConversationLogMessage): ThreadRootMessage {\n return {\n text: message.text,\n userName: message.userName,\n user: message.user,\n loggedAt: message.date ? new Date(message.date).getTime() : undefined,\n };\n}\n\nasync function forkThreadSessionFromRootWithRetry(\n sourceSessionFile: string,\n targetSessionFile: string,\n cwd: string,\n rootMessage: ConversationLogMessage,\n sleep: (ms: number) => Promise<void>,\n retryCount: number,\n retryDelayMs: number,\n): Promise<string> {\n const seed = buildThreadRootSeed(rootMessage);\n\n for (let attempt = 0; attempt < retryCount; attempt++) {\n try {\n return forkThreadSessionFileFromRootMessage(sourceSessionFile, targetSessionFile, cwd, seed);\n } catch (error) {\n if (!(error instanceof ThreadRootNotFoundError)) throw error;\n if (attempt === retryCount - 1) break;\n await sleep(retryDelayMs);\n }\n }\n\n return createThreadSessionFileFromRootMessage(targetSessionFile, cwd, seed, sourceSessionFile);\n}\n\nfunction createThreadSessionFromRootOrEmpty(\n threadFile: string,\n cwd: string,\n threadRootMessage: ThreadRootMessage | null,\n parentSession?: string,\n): string {\n if (threadRootMessage) {\n return createThreadSessionFileFromRootMessage(\n threadFile,\n cwd,\n threadRootMessage,\n parentSession,\n );\n }\n return createManagedSessionFileAtPath(threadFile, cwd);\n}\n\nexport function hasMaterializedSlackBranchSession(\n conversationDir: string,\n sessionKey: string,\n): boolean {\n if (!sessionKey.includes(\":\")) return false;\n return tryResolveThreadSession(getThreadSessionFile(conversationDir, sessionKey)) !== null;\n}\n\nexport async function waitForSlackBranchBootstrap(\n options: SlackBranchBootstrapWaitOptions,\n): Promise<boolean> {\n const {\n parentSessionKey,\n sessionKey,\n hasThreadSession,\n isParentRunning,\n sleep = defaultSleep,\n pollMs = 100,\n } = options;\n\n if (!sessionKey.includes(\":\")) return false;\n if (sessionKey === parentSessionKey) return false;\n if (hasThreadSession()) return false;\n\n let waited = false;\n while (isParentRunning() && !hasThreadSession()) {\n waited = true;\n await sleep(pollMs);\n }\n\n return waited;\n}\n\nexport async function resolveSlackSessionScope(\n options: ResolveSlackSessionScopeOptions,\n): Promise<SlackResolvedSessionScope> {\n const {\n conversationDir,\n sessionKey,\n sleep = defaultSleep,\n retryCount = 5,\n retryDelayMs = 100,\n } = options;\n const cwd = options.cwd ?? conversationDir;\n\n const sessionDir = getChannelSessionDir(conversationDir);\n if (!sessionKey.includes(\":\")) {\n return {\n sessionDir,\n contextFile: resolveManagedSessionFile(sessionDir, cwd),\n threadRootMessage: null,\n };\n }\n\n const rootTs = extractSessionSuffix(sessionKey);\n const threadRootLogMessage = await findLogMessageById(conversationDir, rootTs);\n const threadRootMessage = threadRootLogMessage ? buildThreadRootSeed(threadRootLogMessage) : null;\n const threadFile = getThreadSessionFile(conversationDir, sessionKey);\n const existing = tryResolveThreadSession(threadFile);\n if (existing) {\n return { sessionDir, contextFile: existing, threadRootMessage };\n }\n\n const conversationSource = resolveChannelSessionFile(conversationDir);\n if (!conversationSource) {\n return {\n sessionDir,\n contextFile: createThreadSessionFromRootOrEmpty(threadFile, cwd, threadRootMessage),\n threadRootMessage,\n };\n }\n\n try {\n const contextFile = threadRootMessage\n ? await forkThreadSessionFromRootWithRetry(\n conversationSource,\n threadFile,\n cwd,\n threadRootLogMessage!,\n sleep,\n retryCount,\n retryDelayMs,\n )\n : forkThreadSessionFile(conversationSource, threadFile, cwd);\n return { sessionDir, contextFile, threadRootMessage };\n } catch {\n return {\n sessionDir,\n contextFile: createThreadSessionFromRootOrEmpty(\n threadFile,\n cwd,\n threadRootMessage,\n conversationSource,\n ),\n threadRootMessage,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"branch-manager.js","sourceRoot":"","sources":["../../../src/adapters/slack/branch-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createManagedSessionFileAtPath,\n createThreadSessionFileFromRootMessage,\n forkThreadSessionFile,\n forkThreadSessionFileFromRootMessage,\n getChannelSessionDir,\n getThreadSessionFile,\n resolveChannelSessionFile,\n resolveManagedSessionFile,\n type ResolvedSessionScope,\n ThreadRootNotFoundError,\n tryResolveThreadSession,\n type ThreadRootMessage,\n} from \"../../session-store.js\";\nimport { findLogMessageById, type ConversationLogMessage } from \"../../context.js\";\nimport { parseSlackSessionKey } from \"./session.js\";\n\nexport interface SlackBranchBootstrapWaitOptions {\n parentSessionKey: string;\n sessionKey: string;\n hasThreadSession: () => boolean;\n isParentRunning: () => boolean;\n sleep?: (ms: number) => Promise<void>;\n pollMs?: number;\n}\n\nexport type SlackResolvedSessionScope = ResolvedSessionScope;\n\nexport interface ResolveSlackSessionScopeOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n sleep?: (ms: number) => Promise<void>;\n retryCount?: number;\n retryDelayMs?: number;\n}\n\nexport interface RegisterSlackForkSessionOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction buildThreadRootSeed(message: ConversationLogMessage): ThreadRootMessage {\n return {\n text: message.text,\n userName: message.userName,\n user: message.user,\n loggedAt: message.date ? new Date(message.date).getTime() : undefined,\n };\n}\n\nasync function forkThreadSessionFromRootWithRetry(\n sourceSessionFile: string,\n targetSessionFile: string,\n cwd: string,\n rootMessage: ConversationLogMessage,\n sleep: (ms: number) => Promise<void>,\n retryCount: number,\n retryDelayMs: number,\n): Promise<string> {\n const seed = buildThreadRootSeed(rootMessage);\n\n for (let attempt = 0; attempt < retryCount; attempt++) {\n try {\n return forkThreadSessionFileFromRootMessage(sourceSessionFile, targetSessionFile, cwd, seed);\n } catch (error) {\n if (!(error instanceof ThreadRootNotFoundError)) throw error;\n if (attempt === retryCount - 1) break;\n await sleep(retryDelayMs);\n }\n }\n\n return createThreadSessionFileFromRootMessage(targetSessionFile, cwd, seed, sourceSessionFile);\n}\n\nfunction createThreadSessionFromRootOrEmpty(\n threadFile: string,\n cwd: string,\n threadRootMessage: ThreadRootMessage | null,\n parentSession?: string,\n): string {\n if (threadRootMessage) {\n return createThreadSessionFileFromRootMessage(\n threadFile,\n cwd,\n threadRootMessage,\n parentSession,\n );\n }\n return createManagedSessionFileAtPath(threadFile, cwd);\n}\n\nexport function hasMaterializedSlackBranchSession(\n conversationDir: string,\n sessionKey: string,\n): boolean {\n if (parseSlackSessionKey(sessionKey).kind !== \"fork\") return false;\n return tryResolveThreadSession(getThreadSessionFile(conversationDir, sessionKey)) !== null;\n}\n\nexport function registerSlackForkSession(options: RegisterSlackForkSessionOptions): string | null {\n const { conversationDir, sessionKey } = options;\n if (parseSlackSessionKey(sessionKey).kind !== \"fork\") return null;\n\n const threadFile = getThreadSessionFile(conversationDir, sessionKey);\n return (\n tryResolveThreadSession(threadFile) ??\n createManagedSessionFileAtPath(threadFile, options.cwd ?? conversationDir)\n );\n}\n\nexport async function waitForSlackBranchBootstrap(\n options: SlackBranchBootstrapWaitOptions,\n): Promise<boolean> {\n const {\n parentSessionKey,\n sessionKey,\n hasThreadSession,\n isParentRunning,\n sleep = defaultSleep,\n pollMs = 100,\n } = options;\n\n if (parseSlackSessionKey(sessionKey).kind !== \"fork\") return false;\n if (sessionKey === parentSessionKey) return false;\n if (hasThreadSession()) return false;\n\n let waited = false;\n while (isParentRunning() && !hasThreadSession()) {\n waited = true;\n await sleep(pollMs);\n }\n\n return waited;\n}\n\nexport async function resolveSlackSessionScope(\n options: ResolveSlackSessionScopeOptions,\n): Promise<SlackResolvedSessionScope> {\n const {\n conversationDir,\n sessionKey,\n sleep = defaultSleep,\n retryCount = 5,\n retryDelayMs = 100,\n } = options;\n const cwd = options.cwd ?? conversationDir;\n\n const sessionDir = getChannelSessionDir(conversationDir);\n const sessionRef = parseSlackSessionKey(sessionKey);\n if (sessionRef.kind === \"channel\") {\n return {\n sessionDir,\n contextFile: resolveManagedSessionFile(sessionDir, cwd),\n threadRootMessage: null,\n };\n }\n\n const rootTs = sessionRef.anchorTs;\n const threadRootLogMessage = await findLogMessageById(conversationDir, rootTs);\n const threadRootMessage = threadRootLogMessage ? buildThreadRootSeed(threadRootLogMessage) : null;\n const threadFile = getThreadSessionFile(conversationDir, sessionKey);\n const existing = tryResolveThreadSession(threadFile);\n if (existing) {\n return { sessionDir, contextFile: existing, threadRootMessage };\n }\n\n const conversationSource = resolveChannelSessionFile(conversationDir);\n if (!conversationSource) {\n return {\n sessionDir,\n contextFile: createThreadSessionFromRootOrEmpty(threadFile, cwd, threadRootMessage),\n threadRootMessage,\n };\n }\n\n try {\n const contextFile = threadRootMessage\n ? await forkThreadSessionFromRootWithRetry(\n conversationSource,\n threadFile,\n cwd,\n threadRootLogMessage!,\n sleep,\n retryCount,\n retryDelayMs,\n )\n : forkThreadSessionFile(conversationSource, threadFile, cwd);\n return { sessionDir, contextFile, threadRootMessage };\n } catch {\n return {\n sessionDir,\n contextFile: createThreadSessionFromRootOrEmpty(\n threadFile,\n cwd,\n threadRootMessage,\n conversationSource,\n ),\n threadRootMessage,\n };\n }\n}\n"]}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ChatMessage, ChatResponseContext, PlatformInfo } from "../../adapter.js";
|
|
2
2
|
import type { SlackBot, SlackEvent } from "./bot.js";
|
|
3
3
|
export declare const SLACK_FORMATTING_GUIDE = "## Slack Formatting (mrkdwn, NOT Markdown)\nBold: *text*, Italic: _text_, Code: `code`, Block: ```code```, Links: <url|text>\nDo NOT use **double asterisks** or [markdown](links).";
|
|
4
|
-
export
|
|
4
|
+
export interface SlackAdapterOptions {
|
|
5
|
+
initialMessageTs?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function createSlackAdapters(event: SlackEvent, slack: SlackBot, adapterOptions?: SlackAdapterOptions): {
|
|
5
8
|
message: ChatMessage;
|
|
6
9
|
responseCtx: ChatResponseContext;
|
|
7
10
|
platform: PlatformInfo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EAEnB,YAAY,EACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGrD,eAAO,MAAM,sBAAsB,wLAEmB,CAAC;AAmEvD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,QAAQ,EACf,gBAAgB,CAAC,EAAE,OAAO,GACzB;IACD,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,mBAAmB,CAAC;IACjC,QAAQ,EAAE,YAAY,CAAC;CACxB,CAsSA","sourcesContent":["import type {\n ChatMessage,\n ChatResponseContext,\n ChatToolResult,\n PlatformInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { formatToolArgs, splitText } from \"../shared.js\";\nimport type { SlackBot, SlackEvent } from \"./bot.js\";\nimport { resolveSlackRootTs, resolveSlackSessionKey } from \"./session.js\";\n\nexport const SLACK_FORMATTING_GUIDE = `## Slack Formatting (mrkdwn, NOT Markdown)\nBold: *text*, Italic: _text_, Code: \\`code\\`, Block: \\`\\`\\`code\\`\\`\\`, Links: <url|text>\nDo NOT use **double asterisks** or [markdown](links).`;\n\nconst MAX_MAIN_LENGTH = 35000; // Best-effort streaming cap; final responses use Slack error-driven fallback.\nconst MAX_THREAD_LENGTH = 20000;\nconst FALLBACK_MAIN_LENGTH = 3000;\nconst WORKING_INDICATOR = \" ...\";\nconst TRUNCATION_NOTE_INCREMENTAL =\n \"\\n\\n_(message truncated, ask me to elaborate on specific parts)_\";\n\nconst formatSlackContinuation = (partNum: number): string => `_(continued ${partNum})_`;\n\nfunction isSlackMessageTs(ts: string | undefined): ts is string {\n return typeof ts === \"string\" && /^\\d+\\.\\d+$/.test(ts);\n}\n\nfunction isSlackMsgTooLong(err: unknown): boolean {\n const data = (err as { data?: { error?: string } } | undefined)?.data;\n const message = err instanceof Error ? err.message : String(err);\n return data?.error === \"msg_too_long\" || message.includes(\"msg_too_long\");\n}\n\nfunction fallbackLongSlackText(\n text: string,\n overflowLink?: string,\n prefixLength = FALLBACK_MAIN_LENGTH,\n): string {\n const suffix = overflowLink\n ? `\\n\\n_(message too long for Slack; continued in thread; session view: <${overflowLink}|open>)_`\n : \"\\n\\n_(message too long for Slack; continued in thread)_\";\n return `${text.slice(0, prefixLength)}${suffix}`;\n}\n\nasync function postSlackTextWithFallback(\n post: (text: string) => Promise<string | void>,\n text: string,\n overflowLink?: string,\n): Promise<{ result: string | void; text: string; prefixLength: number }> {\n let prefixLength = FALLBACK_MAIN_LENGTH;\n let lastErr: unknown;\n\n for (;;) {\n const fallbackText = fallbackLongSlackText(text, overflowLink, prefixLength);\n try {\n const result = await post(fallbackText);\n return { result, text: fallbackText, prefixLength };\n } catch (err) {\n if (!isSlackMsgTooLong(err)) throw err;\n lastErr = err;\n if (prefixLength === 0) {\n throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));\n }\n prefixLength = Math.max(0, Math.floor(prefixLength / 2));\n }\n }\n}\n\nfunction formatSlackToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n let text = `*${result.isError ? \"✗\" : \"✓\"} ${result.toolName}*`;\n if (result.label) text += `: ${result.label}`;\n text += ` (${duration}s)\\n`;\n if (argsFormatted) text += `\\`\\`\\`\\n${argsFormatted}\\n\\`\\`\\`\\n`;\n text += `*Result:*\\n\\`\\`\\`\\n${result.result}\\n\\`\\`\\``;\n return text;\n}\n\nexport function createSlackAdapters(\n event: SlackEvent,\n slack: SlackBot,\n isSyntheticEvent?: boolean,\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n let messageTs: string | null = null;\n let assistantStatusFailureWarned = false;\n const onAssistantStatusError = (label: string, err: unknown): void => {\n if (assistantStatusFailureWarned) return;\n assistantStatusFailureWarned = true;\n log.logWarning(\n `Slack setAssistantStatus failed (${label}; further occurrences suppressed for this session)`,\n err instanceof Error ? err.message : String(err),\n );\n };\n const threadMessageTs: string[] = [];\n let accumulatedText = \"\";\n let isWorking = true;\n let updatePromise = Promise.resolve();\n\n const channelId = event.channel;\n const conversationId = event.conversationId;\n const user = slack.getUser(event.user);\n\n // Synthetic event ts format: `event:<filename>`.\n const eventFilename = isSyntheticEvent\n ? event.ts.match(/^event:([^:]+(?:\\.json)?)/)?.[1]\n : undefined;\n\n const rootTs =\n event.thread_ts ?? (isSlackMessageTs(event.ts) ? resolveSlackRootTs(event.ts) : undefined);\n const isThreaded = !!event.thread_ts;\n\n /**\n * Post the first visible reply.\n * Default Slack behavior is now top-level channel replies.\n * If the triggering message is already inside a thread, stay in that thread.\n * Synthetic event messages have no real Slack root ts, so they must post top-level.\n */\n const postFirstMessage = async (text: string): Promise<string> => {\n if (isSyntheticEvent) {\n if (event.thread_ts) {\n return slack.postInThread(channelId, event.thread_ts, text);\n }\n return slack.postMessage(channelId, text);\n }\n if (isThreaded && rootTs) {\n return slack.postInThread(channelId, rootTs, text);\n }\n return slack.postMessage(channelId, text);\n };\n\n const postDiagnosticDirect = async (\n text: string,\n options?: { style?: \"muted\" | \"error\" },\n ): Promise<void> => {\n const threadAnchor = messageTs ?? rootTs;\n if (!threadAnchor) return;\n\n for (const part of splitText(text, MAX_THREAD_LENGTH, formatSlackContinuation)) {\n if (options?.style === \"muted\") {\n const CONTEXT_TEXT_LIMIT = 3000;\n const blockText =\n part.length > CONTEXT_TEXT_LIMIT\n ? part.substring(0, CONTEXT_TEXT_LIMIT - 20) + \"\\n_(truncated)_\"\n : part;\n const ts = await slack.postInThreadBlocks(channelId, threadAnchor, part, [\n { type: \"context\", elements: [{ type: \"mrkdwn\", text: blockText }] },\n ]);\n threadMessageTs.push(ts);\n } else {\n const diagnosticText = options?.style === \"error\" ? `_${part}_` : part;\n const ts = await slack.postInThread(channelId, threadAnchor, diagnosticText);\n threadMessageTs.push(ts);\n }\n }\n };\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey: isSyntheticEvent\n ? `${conversationId}:${event.ts}`\n : (event.sessionKey ?? resolveSlackSessionKey(conversationId, event.thread_ts)),\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: user?.userName,\n text: event.text,\n attachments: (event.attachments || []).map((a) => ({\n name: a.original,\n localPath: a.localPath,\n })),\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"slack\",\n formattingGuide: SLACK_FORMATTING_GUIDE,\n channels: slack.getAllChannels().map((c) => ({ id: c.id, name: c.name })),\n users: slack\n .getAllUsers()\n .map((u) => ({ id: u.id, userName: u.userName, displayName: u.displayName })),\n diagnostics: {\n showUsageSummary: true,\n },\n };\n\n const responseCtx = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n\n const mainLimit = isWorking\n ? MAX_MAIN_LENGTH - WORKING_INDICATOR.length\n : MAX_MAIN_LENGTH;\n if (accumulatedText.length > mainLimit) {\n accumulatedText =\n accumulatedText.substring(0, mainLimit - TRUNCATION_NOTE_INCREMENTAL.length) +\n TRUNCATION_NOTE_INCREMENTAL;\n }\n\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n\n if (messageTs) {\n await slack.updateMessage(channelId, messageTs, displayText);\n } else if (isThreaded && rootTs) {\n // Reply within the user's thread\n messageTs = await slack.postInThread(channelId, rootTs, displayText);\n } else {\n messageTs = await postFirstMessage(displayText);\n if (isSyntheticEvent && !event.thread_ts && messageTs) {\n slack.aliasSyntheticEventThread(channelId, messageTs, event.ts);\n }\n }\n\n if (messageTs) {\n slack.logBotResponse(channelId, text, messageTs, isThreaded ? rootTs : undefined);\n }\n } catch (err) {\n log.logWarning(\"Slack respond error\", err instanceof Error ? err.message : String(err));\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string, options?: { createOverflowLink?: () => string }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n // Lazy: only mint a token if Slack actually rejects the message.\n let overflowLink: string | undefined;\n const resolveOverflowLink = (): string | undefined => {\n if (overflowLink === undefined && options?.createOverflowLink) {\n overflowLink = options.createOverflowLink();\n }\n return overflowLink;\n };\n\n const postOrUpdate = async (body: string): Promise<void> => {\n if (messageTs) {\n await slack.updateMessage(channelId, messageTs, body);\n return;\n }\n if (isThreaded && rootTs) {\n messageTs = await slack.postInThread(channelId, rootTs, body);\n return;\n }\n messageTs = await postFirstMessage(body);\n if (isSyntheticEvent && !event.thread_ts && messageTs) {\n slack.aliasSyntheticEventThread(channelId, messageTs, event.ts);\n }\n };\n\n accumulatedText = text;\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n\n try {\n await postOrUpdate(displayText);\n } catch (err) {\n if (!isSlackMsgTooLong(err)) throw err;\n const link = resolveOverflowLink();\n const fallback = await postSlackTextWithFallback(\n async (body) => {\n await postOrUpdate(body);\n },\n text,\n link,\n );\n accumulatedText = fallback.text;\n const continuation = text.slice(fallback.prefixLength).trimStart();\n if (continuation) {\n await postDiagnosticDirect(`_(continued from truncated message)_\\n\\n${continuation}`);\n }\n }\n } catch (err) {\n log.logWarning(\n \"Slack replaceResponse error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n await postDiagnosticDirect(text, options);\n } catch (err) {\n log.logWarning(\n \"Slack respondDiagnostic error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responseCtx.respondDiagnostic(formatSlackToolResult(result));\n },\n\n setTyping: async (isTyping: boolean) => {\n if (isTyping && !messageTs && rootTs) {\n try {\n const statusText = eventFilename ? `Starting event: ${eventFilename}` : \"Thinking\";\n await slack.setAssistantStatus(channelId, rootTs, statusText);\n } catch (err) {\n // Assistant API not available — first respond() call will create the message.\n onAssistantStatusError(\"typing\", err);\n }\n }\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await slack.uploadFile(channelId, filePath, title, rootTs);\n },\n\n setWorking: async (working: boolean) => {\n updatePromise = updatePromise.then(async () => {\n try {\n isWorking = working;\n if (messageTs) {\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n const updates: Promise<void>[] = [\n slack.updateMessage(channelId, messageTs, displayText),\n ];\n if (!working) {\n if (rootTs) {\n updates.push(\n slack\n .setAssistantStatus(channelId, rootTs, \"\")\n .catch((err) => onAssistantStatusError(\"clear-on-idle\", err)),\n );\n }\n }\n await Promise.all(updates);\n }\n } catch (err) {\n log.logWarning(\n \"Slack setWorking error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n // Clear assistant status first\n if (rootTs) {\n try {\n await slack.setAssistantStatus(channelId, rootTs, \"\");\n } catch {\n // Ignore errors clearing status\n }\n }\n\n // Delete thread messages first (in reverse order)\n for (let i = threadMessageTs.length - 1; i >= 0; i--) {\n try {\n await slack.deleteMessage(channelId, threadMessageTs[i]);\n } catch {\n // Ignore errors deleting thread messages\n }\n }\n threadMessageTs.length = 0;\n // Then delete main message\n if (messageTs) {\n await slack.deleteMessage(channelId, messageTs);\n messageTs = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EAEnB,YAAY,EACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGrD,eAAO,MAAM,sBAAsB,wLAEmB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AA+DD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,QAAQ,EACf,cAAc,GAAE,mBAAwB,GACvC;IACD,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,mBAAmB,CAAC;IACjC,QAAQ,EAAE,YAAY,CAAC;CACxB,CAgTA","sourcesContent":["import type {\n ChatMessage,\n ChatResponseContext,\n ChatToolResult,\n PlatformInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { createChatResponseErrorReporter, formatToolArgs, splitText } from \"../shared.js\";\nimport type { SlackBot, SlackEvent } from \"./bot.js\";\nimport { planSlackAdapterSession } from \"./session.js\";\n\nexport const SLACK_FORMATTING_GUIDE = `## Slack Formatting (mrkdwn, NOT Markdown)\nBold: *text*, Italic: _text_, Code: \\`code\\`, Block: \\`\\`\\`code\\`\\`\\`, Links: <url|text>\nDo NOT use **double asterisks** or [markdown](links).`;\n\nexport interface SlackAdapterOptions {\n initialMessageTs?: string;\n}\n\nconst MAX_MAIN_LENGTH = 35000; // Best-effort streaming cap; final responses use Slack error-driven fallback.\nconst MAX_THREAD_LENGTH = 20000;\nconst FALLBACK_MAIN_LENGTH = 3000;\nconst WORKING_INDICATOR = \" ...\";\nconst TRUNCATION_NOTE_INCREMENTAL =\n \"\\n\\n_(message truncated, ask me to elaborate on specific parts)_\";\n\nconst formatSlackContinuation = (partNum: number): string => `_(continued ${partNum})_`;\n\nfunction isSlackMsgTooLong(err: unknown): boolean {\n const data = (err as { data?: { error?: string } } | undefined)?.data;\n const message = err instanceof Error ? err.message : String(err);\n return data?.error === \"msg_too_long\" || message.includes(\"msg_too_long\");\n}\n\nfunction fallbackLongSlackText(\n text: string,\n overflowLink?: string,\n prefixLength = FALLBACK_MAIN_LENGTH,\n): string {\n const suffix = overflowLink\n ? `\\n\\n_(message too long for Slack; continued in thread; session view: <${overflowLink}|open>)_`\n : \"\\n\\n_(message too long for Slack; continued in thread)_\";\n return `${text.slice(0, prefixLength)}${suffix}`;\n}\n\nasync function postSlackTextWithFallback(\n post: (text: string) => Promise<string | void>,\n text: string,\n overflowLink?: string,\n): Promise<{ result: string | void; text: string; prefixLength: number }> {\n let prefixLength = FALLBACK_MAIN_LENGTH;\n let lastErr: unknown;\n\n for (;;) {\n const fallbackText = fallbackLongSlackText(text, overflowLink, prefixLength);\n try {\n const result = await post(fallbackText);\n return { result, text: fallbackText, prefixLength };\n } catch (err) {\n if (!isSlackMsgTooLong(err)) throw err;\n lastErr = err;\n if (prefixLength === 0) {\n throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));\n }\n prefixLength = Math.max(0, Math.floor(prefixLength / 2));\n }\n }\n}\n\nfunction formatSlackToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n let text = `*${result.isError ? \"✗\" : \"✓\"} ${result.toolName}*`;\n if (result.label) text += `: ${result.label}`;\n text += ` (${duration}s)\\n`;\n if (argsFormatted) text += `\\`\\`\\`\\n${argsFormatted}\\n\\`\\`\\`\\n`;\n text += `*Result:*\\n\\`\\`\\`\\n${result.result}\\n\\`\\`\\``;\n return text;\n}\n\nexport function createSlackAdapters(\n event: SlackEvent,\n slack: SlackBot,\n adapterOptions: SlackAdapterOptions = {},\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n const sessionPlan = planSlackAdapterSession(event, {\n initialMessageTs: adapterOptions.initialMessageTs,\n });\n let messageTs: string | null = sessionPlan.initialMessageTs ?? null;\n let assistantStatusFailureWarned = false;\n const onAssistantStatusError = (label: string, err: unknown): void => {\n if (assistantStatusFailureWarned) return;\n assistantStatusFailureWarned = true;\n log.logWarning(\n `Slack setAssistantStatus failed (${label}; further occurrences suppressed for this session)`,\n err instanceof Error ? err.message : String(err),\n );\n };\n const threadMessageTs: string[] = [];\n let accumulatedText = \"\";\n let isWorking = true;\n let updatePromise = Promise.resolve();\n\n const channelId = event.channel;\n const conversationId = event.conversationId;\n const user = slack.getUser(event.user);\n\n // Slack message timestamps are numeric; event-file triggers use `event:<filename>`.\n const eventFilename = event.ts.match(/^event:([^:]+(?:\\.json)?)/)?.[1];\n\n const { rootTs, isThreaded } = sessionPlan;\n\n /**\n * Post the first visible reply.\n * Default Slack behavior is now top-level channel replies.\n * If the triggering message is already inside a thread, stay in that thread.\n */\n const postFirstMessage = async (text: string): Promise<string> => {\n if (isThreaded && rootTs) {\n return slack.postInThread(channelId, rootTs, text);\n }\n return slack.postMessage(channelId, text);\n };\n\n const postDiagnosticDirect = async (\n text: string,\n options?: { style?: \"muted\" | \"error\" },\n ): Promise<void> => {\n const threadAnchor = messageTs ?? rootTs;\n if (!threadAnchor) return;\n\n for (const part of splitText(text, MAX_THREAD_LENGTH, formatSlackContinuation)) {\n if (options?.style === \"muted\") {\n const CONTEXT_TEXT_LIMIT = 3000;\n const blockText =\n part.length > CONTEXT_TEXT_LIMIT\n ? part.substring(0, CONTEXT_TEXT_LIMIT - 20) + \"\\n_(truncated)_\"\n : part;\n const ts = await slack.postInThreadBlocks(channelId, threadAnchor, part, [\n { type: \"context\", elements: [{ type: \"mrkdwn\", text: blockText }] },\n ]);\n threadMessageTs.push(ts);\n } else {\n const diagnosticText = options?.style === \"error\" ? `_${part}_` : part;\n const ts = await slack.postInThread(channelId, threadAnchor, diagnosticText);\n threadMessageTs.push(ts);\n }\n }\n };\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey: sessionPlan.sessionKey,\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: user?.userName,\n text: event.text,\n attachments: (event.attachments || []).map((a) => ({\n name: a.original,\n localPath: a.localPath,\n })),\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"slack\",\n formattingGuide: SLACK_FORMATTING_GUIDE,\n channels: slack.getAllChannels().map((c) => ({ id: c.id, name: c.name })),\n users: slack\n .getAllUsers()\n .map((u) => ({ id: u.id, userName: u.userName, displayName: u.displayName })),\n diagnostics: {\n showUsageSummary: true,\n },\n };\n\n const reportResponseError = createChatResponseErrorReporter(() => ({\n platform: \"slack\",\n conversationId,\n channelId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: messageTs,\n threadTs: rootTs,\n conversationKind: message.conversationKind,\n isThreaded,\n }));\n\n const responseCtx = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n\n const mainLimit = isWorking\n ? MAX_MAIN_LENGTH - WORKING_INDICATOR.length\n : MAX_MAIN_LENGTH;\n if (accumulatedText.length > mainLimit) {\n accumulatedText =\n accumulatedText.substring(0, mainLimit - TRUNCATION_NOTE_INCREMENTAL.length) +\n TRUNCATION_NOTE_INCREMENTAL;\n }\n\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n\n if (messageTs) {\n await slack.updateMessage(channelId, messageTs, displayText);\n } else if (isThreaded && rootTs) {\n // Reply within the user's thread\n messageTs = await slack.postInThread(channelId, rootTs, displayText);\n } else {\n messageTs = await postFirstMessage(displayText);\n }\n\n if (messageTs) {\n slack.logBotResponse(channelId, text, messageTs, isThreaded ? rootTs : undefined);\n }\n } catch (err) {\n log.logWarning(\"Slack respond error\", err instanceof Error ? err.message : String(err));\n reportResponseError(err, \"respond\", {\n phase: messageTs ? \"update\" : \"initial_post\",\n textLength: text.length,\n accumulatedLength: accumulatedText.length,\n });\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string, options?: { createOverflowLink?: () => string }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n // Lazy: only mint a token if Slack actually rejects the message.\n let overflowLink: string | undefined;\n const resolveOverflowLink = (): string | undefined => {\n if (overflowLink === undefined && options?.createOverflowLink) {\n overflowLink = options.createOverflowLink();\n }\n return overflowLink;\n };\n\n const postOrUpdate = async (body: string): Promise<void> => {\n if (messageTs) {\n await slack.updateMessage(channelId, messageTs, body);\n return;\n }\n if (isThreaded && rootTs) {\n messageTs = await slack.postInThread(channelId, rootTs, body);\n return;\n }\n messageTs = await postFirstMessage(body);\n };\n\n accumulatedText = text;\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n\n try {\n await postOrUpdate(displayText);\n } catch (err) {\n if (!isSlackMsgTooLong(err)) throw err;\n const link = resolveOverflowLink();\n const fallback = await postSlackTextWithFallback(\n async (body) => {\n await postOrUpdate(body);\n },\n text,\n link,\n );\n accumulatedText = fallback.text;\n const continuation = text.slice(fallback.prefixLength).trimStart();\n if (continuation) {\n await postDiagnosticDirect(`_(continued from truncated message)_\\n\\n${continuation}`);\n }\n }\n } catch (err) {\n log.logWarning(\n \"Slack replaceResponse error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"replace_response\", {\n textLength: text.length,\n hadExistingResponse: Boolean(messageTs),\n });\n }\n });\n await updatePromise;\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n await postDiagnosticDirect(text, options);\n } catch (err) {\n log.logWarning(\n \"Slack respondDiagnostic error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"respond_diagnostic\", {\n textLength: text.length,\n style: options?.style,\n });\n }\n });\n await updatePromise;\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responseCtx.respondDiagnostic(formatSlackToolResult(result));\n },\n\n setTyping: async (isTyping: boolean) => {\n if (isTyping && !messageTs && rootTs) {\n try {\n const statusText = eventFilename ? `Starting event: ${eventFilename}` : \"Thinking\";\n await slack.setAssistantStatus(channelId, rootTs, statusText);\n } catch (err) {\n // Assistant API not available — first respond() call will create the message.\n onAssistantStatusError(\"typing\", err);\n }\n }\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await slack.uploadFile(channelId, filePath, title, rootTs);\n },\n\n setWorking: async (working: boolean) => {\n updatePromise = updatePromise.then(async () => {\n try {\n isWorking = working;\n if (messageTs) {\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n const updates: Promise<void>[] = [\n slack.updateMessage(channelId, messageTs, displayText),\n ];\n if (!working) {\n if (rootTs) {\n updates.push(\n slack\n .setAssistantStatus(channelId, rootTs, \"\")\n .catch((err) => onAssistantStatusError(\"clear-on-idle\", err)),\n );\n }\n }\n await Promise.all(updates);\n }\n } catch (err) {\n log.logWarning(\n \"Slack setWorking error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"set_working\", { working });\n }\n });\n await updatePromise;\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n // Clear assistant status first\n if (rootTs) {\n try {\n await slack.setAssistantStatus(channelId, rootTs, \"\");\n } catch {\n // Ignore errors clearing status\n }\n }\n\n // Delete thread messages first (in reverse order)\n for (let i = threadMessageTs.length - 1; i >= 0; i--) {\n try {\n await slack.deleteMessage(channelId, threadMessageTs[i]);\n } catch {\n // Ignore errors deleting thread messages\n }\n }\n threadMessageTs.length = 0;\n // Then delete main message\n if (messageTs) {\n await slack.deleteMessage(channelId, messageTs);\n messageTs = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as log from "../../log.js";
|
|
2
|
-
import { formatToolArgs, splitText } from "../shared.js";
|
|
3
|
-
import {
|
|
2
|
+
import { createChatResponseErrorReporter, formatToolArgs, splitText } from "../shared.js";
|
|
3
|
+
import { planSlackAdapterSession } from "./session.js";
|
|
4
4
|
export const SLACK_FORMATTING_GUIDE = `## Slack Formatting (mrkdwn, NOT Markdown)
|
|
5
5
|
Bold: *text*, Italic: _text_, Code: \`code\`, Block: \`\`\`code\`\`\`, Links: <url|text>
|
|
6
6
|
Do NOT use **double asterisks** or [markdown](links).`;
|
|
@@ -10,9 +10,6 @@ const FALLBACK_MAIN_LENGTH = 3000;
|
|
|
10
10
|
const WORKING_INDICATOR = " ...";
|
|
11
11
|
const TRUNCATION_NOTE_INCREMENTAL = "\n\n_(message truncated, ask me to elaborate on specific parts)_";
|
|
12
12
|
const formatSlackContinuation = (partNum) => `_(continued ${partNum})_`;
|
|
13
|
-
function isSlackMessageTs(ts) {
|
|
14
|
-
return typeof ts === "string" && /^\d+\.\d+$/.test(ts);
|
|
15
|
-
}
|
|
16
13
|
function isSlackMsgTooLong(err) {
|
|
17
14
|
const data = err?.data;
|
|
18
15
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -56,8 +53,11 @@ function formatSlackToolResult(result) {
|
|
|
56
53
|
text += `*Result:*\n\`\`\`\n${result.result}\n\`\`\``;
|
|
57
54
|
return text;
|
|
58
55
|
}
|
|
59
|
-
export function createSlackAdapters(event, slack,
|
|
60
|
-
|
|
56
|
+
export function createSlackAdapters(event, slack, adapterOptions = {}) {
|
|
57
|
+
const sessionPlan = planSlackAdapterSession(event, {
|
|
58
|
+
initialMessageTs: adapterOptions.initialMessageTs,
|
|
59
|
+
});
|
|
60
|
+
let messageTs = sessionPlan.initialMessageTs ?? null;
|
|
61
61
|
let assistantStatusFailureWarned = false;
|
|
62
62
|
const onAssistantStatusError = (label, err) => {
|
|
63
63
|
if (assistantStatusFailureWarned)
|
|
@@ -72,25 +72,15 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
72
72
|
const channelId = event.channel;
|
|
73
73
|
const conversationId = event.conversationId;
|
|
74
74
|
const user = slack.getUser(event.user);
|
|
75
|
-
//
|
|
76
|
-
const eventFilename =
|
|
77
|
-
|
|
78
|
-
: undefined;
|
|
79
|
-
const rootTs = event.thread_ts ?? (isSlackMessageTs(event.ts) ? resolveSlackRootTs(event.ts) : undefined);
|
|
80
|
-
const isThreaded = !!event.thread_ts;
|
|
75
|
+
// Slack message timestamps are numeric; event-file triggers use `event:<filename>`.
|
|
76
|
+
const eventFilename = event.ts.match(/^event:([^:]+(?:\.json)?)/)?.[1];
|
|
77
|
+
const { rootTs, isThreaded } = sessionPlan;
|
|
81
78
|
/**
|
|
82
79
|
* Post the first visible reply.
|
|
83
80
|
* Default Slack behavior is now top-level channel replies.
|
|
84
81
|
* If the triggering message is already inside a thread, stay in that thread.
|
|
85
|
-
* Synthetic event messages have no real Slack root ts, so they must post top-level.
|
|
86
82
|
*/
|
|
87
83
|
const postFirstMessage = async (text) => {
|
|
88
|
-
if (isSyntheticEvent) {
|
|
89
|
-
if (event.thread_ts) {
|
|
90
|
-
return slack.postInThread(channelId, event.thread_ts, text);
|
|
91
|
-
}
|
|
92
|
-
return slack.postMessage(channelId, text);
|
|
93
|
-
}
|
|
94
84
|
if (isThreaded && rootTs) {
|
|
95
85
|
return slack.postInThread(channelId, rootTs, text);
|
|
96
86
|
}
|
|
@@ -120,9 +110,7 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
120
110
|
};
|
|
121
111
|
const message = {
|
|
122
112
|
id: event.ts,
|
|
123
|
-
sessionKey:
|
|
124
|
-
? `${conversationId}:${event.ts}`
|
|
125
|
-
: (event.sessionKey ?? resolveSlackSessionKey(conversationId, event.thread_ts)),
|
|
113
|
+
sessionKey: sessionPlan.sessionKey,
|
|
126
114
|
conversationKind: event.conversationKind,
|
|
127
115
|
userId: event.user,
|
|
128
116
|
userName: user?.userName,
|
|
@@ -144,6 +132,17 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
144
132
|
showUsageSummary: true,
|
|
145
133
|
},
|
|
146
134
|
};
|
|
135
|
+
const reportResponseError = createChatResponseErrorReporter(() => ({
|
|
136
|
+
platform: "slack",
|
|
137
|
+
conversationId,
|
|
138
|
+
channelId,
|
|
139
|
+
messageId: message.id,
|
|
140
|
+
sessionKey: message.sessionKey,
|
|
141
|
+
responseMessageId: messageTs,
|
|
142
|
+
threadTs: rootTs,
|
|
143
|
+
conversationKind: message.conversationKind,
|
|
144
|
+
isThreaded,
|
|
145
|
+
}));
|
|
147
146
|
const responseCtx = {
|
|
148
147
|
respond: async (text) => {
|
|
149
148
|
updatePromise = updatePromise.then(async () => {
|
|
@@ -167,9 +166,6 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
167
166
|
}
|
|
168
167
|
else {
|
|
169
168
|
messageTs = await postFirstMessage(displayText);
|
|
170
|
-
if (isSyntheticEvent && !event.thread_ts && messageTs) {
|
|
171
|
-
slack.aliasSyntheticEventThread(channelId, messageTs, event.ts);
|
|
172
|
-
}
|
|
173
169
|
}
|
|
174
170
|
if (messageTs) {
|
|
175
171
|
slack.logBotResponse(channelId, text, messageTs, isThreaded ? rootTs : undefined);
|
|
@@ -177,6 +173,11 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
177
173
|
}
|
|
178
174
|
catch (err) {
|
|
179
175
|
log.logWarning("Slack respond error", err instanceof Error ? err.message : String(err));
|
|
176
|
+
reportResponseError(err, "respond", {
|
|
177
|
+
phase: messageTs ? "update" : "initial_post",
|
|
178
|
+
textLength: text.length,
|
|
179
|
+
accumulatedLength: accumulatedText.length,
|
|
180
|
+
});
|
|
180
181
|
}
|
|
181
182
|
});
|
|
182
183
|
await updatePromise;
|
|
@@ -202,9 +203,6 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
202
203
|
return;
|
|
203
204
|
}
|
|
204
205
|
messageTs = await postFirstMessage(body);
|
|
205
|
-
if (isSyntheticEvent && !event.thread_ts && messageTs) {
|
|
206
|
-
slack.aliasSyntheticEventThread(channelId, messageTs, event.ts);
|
|
207
|
-
}
|
|
208
206
|
};
|
|
209
207
|
accumulatedText = text;
|
|
210
208
|
const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;
|
|
@@ -227,6 +225,10 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
227
225
|
}
|
|
228
226
|
catch (err) {
|
|
229
227
|
log.logWarning("Slack replaceResponse error", err instanceof Error ? err.message : String(err));
|
|
228
|
+
reportResponseError(err, "replace_response", {
|
|
229
|
+
textLength: text.length,
|
|
230
|
+
hadExistingResponse: Boolean(messageTs),
|
|
231
|
+
});
|
|
230
232
|
}
|
|
231
233
|
});
|
|
232
234
|
await updatePromise;
|
|
@@ -238,6 +240,10 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
238
240
|
}
|
|
239
241
|
catch (err) {
|
|
240
242
|
log.logWarning("Slack respondDiagnostic error", err instanceof Error ? err.message : String(err));
|
|
243
|
+
reportResponseError(err, "respond_diagnostic", {
|
|
244
|
+
textLength: text.length,
|
|
245
|
+
style: options?.style,
|
|
246
|
+
});
|
|
241
247
|
}
|
|
242
248
|
});
|
|
243
249
|
await updatePromise;
|
|
@@ -281,6 +287,7 @@ export function createSlackAdapters(event, slack, isSyntheticEvent) {
|
|
|
281
287
|
}
|
|
282
288
|
catch (err) {
|
|
283
289
|
log.logWarning("Slack setWorking error", err instanceof Error ? err.message : String(err));
|
|
290
|
+
reportResponseError(err, "set_working", { working });
|
|
284
291
|
}
|
|
285
292
|
});
|
|
286
293
|
await updatePromise;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/slack/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ChatMessage,\n ChatResponseContext,\n ChatToolResult,\n PlatformInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { formatToolArgs, splitText } from \"../shared.js\";\nimport type { SlackBot, SlackEvent } from \"./bot.js\";\nimport { resolveSlackRootTs, resolveSlackSessionKey } from \"./session.js\";\n\nexport const SLACK_FORMATTING_GUIDE = `## Slack Formatting (mrkdwn, NOT Markdown)\nBold: *text*, Italic: _text_, Code: \\`code\\`, Block: \\`\\`\\`code\\`\\`\\`, Links: <url|text>\nDo NOT use **double asterisks** or [markdown](links).`;\n\nconst MAX_MAIN_LENGTH = 35000; // Best-effort streaming cap; final responses use Slack error-driven fallback.\nconst MAX_THREAD_LENGTH = 20000;\nconst FALLBACK_MAIN_LENGTH = 3000;\nconst WORKING_INDICATOR = \" ...\";\nconst TRUNCATION_NOTE_INCREMENTAL =\n \"\\n\\n_(message truncated, ask me to elaborate on specific parts)_\";\n\nconst formatSlackContinuation = (partNum: number): string => `_(continued ${partNum})_`;\n\nfunction isSlackMessageTs(ts: string | undefined): ts is string {\n return typeof ts === \"string\" && /^\\d+\\.\\d+$/.test(ts);\n}\n\nfunction isSlackMsgTooLong(err: unknown): boolean {\n const data = (err as { data?: { error?: string } } | undefined)?.data;\n const message = err instanceof Error ? err.message : String(err);\n return data?.error === \"msg_too_long\" || message.includes(\"msg_too_long\");\n}\n\nfunction fallbackLongSlackText(\n text: string,\n overflowLink?: string,\n prefixLength = FALLBACK_MAIN_LENGTH,\n): string {\n const suffix = overflowLink\n ? `\\n\\n_(message too long for Slack; continued in thread; session view: <${overflowLink}|open>)_`\n : \"\\n\\n_(message too long for Slack; continued in thread)_\";\n return `${text.slice(0, prefixLength)}${suffix}`;\n}\n\nasync function postSlackTextWithFallback(\n post: (text: string) => Promise<string | void>,\n text: string,\n overflowLink?: string,\n): Promise<{ result: string | void; text: string; prefixLength: number }> {\n let prefixLength = FALLBACK_MAIN_LENGTH;\n let lastErr: unknown;\n\n for (;;) {\n const fallbackText = fallbackLongSlackText(text, overflowLink, prefixLength);\n try {\n const result = await post(fallbackText);\n return { result, text: fallbackText, prefixLength };\n } catch (err) {\n if (!isSlackMsgTooLong(err)) throw err;\n lastErr = err;\n if (prefixLength === 0) {\n throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));\n }\n prefixLength = Math.max(0, Math.floor(prefixLength / 2));\n }\n }\n}\n\nfunction formatSlackToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n let text = `*${result.isError ? \"✗\" : \"✓\"} ${result.toolName}*`;\n if (result.label) text += `: ${result.label}`;\n text += ` (${duration}s)\\n`;\n if (argsFormatted) text += `\\`\\`\\`\\n${argsFormatted}\\n\\`\\`\\`\\n`;\n text += `*Result:*\\n\\`\\`\\`\\n${result.result}\\n\\`\\`\\``;\n return text;\n}\n\nexport function createSlackAdapters(\n event: SlackEvent,\n slack: SlackBot,\n isSyntheticEvent?: boolean,\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n let messageTs: string | null = null;\n let assistantStatusFailureWarned = false;\n const onAssistantStatusError = (label: string, err: unknown): void => {\n if (assistantStatusFailureWarned) return;\n assistantStatusFailureWarned = true;\n log.logWarning(\n `Slack setAssistantStatus failed (${label}; further occurrences suppressed for this session)`,\n err instanceof Error ? err.message : String(err),\n );\n };\n const threadMessageTs: string[] = [];\n let accumulatedText = \"\";\n let isWorking = true;\n let updatePromise = Promise.resolve();\n\n const channelId = event.channel;\n const conversationId = event.conversationId;\n const user = slack.getUser(event.user);\n\n // Synthetic event ts format: `event:<filename>`.\n const eventFilename = isSyntheticEvent\n ? event.ts.match(/^event:([^:]+(?:\\.json)?)/)?.[1]\n : undefined;\n\n const rootTs =\n event.thread_ts ?? (isSlackMessageTs(event.ts) ? resolveSlackRootTs(event.ts) : undefined);\n const isThreaded = !!event.thread_ts;\n\n /**\n * Post the first visible reply.\n * Default Slack behavior is now top-level channel replies.\n * If the triggering message is already inside a thread, stay in that thread.\n * Synthetic event messages have no real Slack root ts, so they must post top-level.\n */\n const postFirstMessage = async (text: string): Promise<string> => {\n if (isSyntheticEvent) {\n if (event.thread_ts) {\n return slack.postInThread(channelId, event.thread_ts, text);\n }\n return slack.postMessage(channelId, text);\n }\n if (isThreaded && rootTs) {\n return slack.postInThread(channelId, rootTs, text);\n }\n return slack.postMessage(channelId, text);\n };\n\n const postDiagnosticDirect = async (\n text: string,\n options?: { style?: \"muted\" | \"error\" },\n ): Promise<void> => {\n const threadAnchor = messageTs ?? rootTs;\n if (!threadAnchor) return;\n\n for (const part of splitText(text, MAX_THREAD_LENGTH, formatSlackContinuation)) {\n if (options?.style === \"muted\") {\n const CONTEXT_TEXT_LIMIT = 3000;\n const blockText =\n part.length > CONTEXT_TEXT_LIMIT\n ? part.substring(0, CONTEXT_TEXT_LIMIT - 20) + \"\\n_(truncated)_\"\n : part;\n const ts = await slack.postInThreadBlocks(channelId, threadAnchor, part, [\n { type: \"context\", elements: [{ type: \"mrkdwn\", text: blockText }] },\n ]);\n threadMessageTs.push(ts);\n } else {\n const diagnosticText = options?.style === \"error\" ? `_${part}_` : part;\n const ts = await slack.postInThread(channelId, threadAnchor, diagnosticText);\n threadMessageTs.push(ts);\n }\n }\n };\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey: isSyntheticEvent\n ? `${conversationId}:${event.ts}`\n : (event.sessionKey ?? resolveSlackSessionKey(conversationId, event.thread_ts)),\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: user?.userName,\n text: event.text,\n attachments: (event.attachments || []).map((a) => ({\n name: a.original,\n localPath: a.localPath,\n })),\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"slack\",\n formattingGuide: SLACK_FORMATTING_GUIDE,\n channels: slack.getAllChannels().map((c) => ({ id: c.id, name: c.name })),\n users: slack\n .getAllUsers()\n .map((u) => ({ id: u.id, userName: u.userName, displayName: u.displayName })),\n diagnostics: {\n showUsageSummary: true,\n },\n };\n\n const responseCtx = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n\n const mainLimit = isWorking\n ? MAX_MAIN_LENGTH - WORKING_INDICATOR.length\n : MAX_MAIN_LENGTH;\n if (accumulatedText.length > mainLimit) {\n accumulatedText =\n accumulatedText.substring(0, mainLimit - TRUNCATION_NOTE_INCREMENTAL.length) +\n TRUNCATION_NOTE_INCREMENTAL;\n }\n\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n\n if (messageTs) {\n await slack.updateMessage(channelId, messageTs, displayText);\n } else if (isThreaded && rootTs) {\n // Reply within the user's thread\n messageTs = await slack.postInThread(channelId, rootTs, displayText);\n } else {\n messageTs = await postFirstMessage(displayText);\n if (isSyntheticEvent && !event.thread_ts && messageTs) {\n slack.aliasSyntheticEventThread(channelId, messageTs, event.ts);\n }\n }\n\n if (messageTs) {\n slack.logBotResponse(channelId, text, messageTs, isThreaded ? rootTs : undefined);\n }\n } catch (err) {\n log.logWarning(\"Slack respond error\", err instanceof Error ? err.message : String(err));\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string, options?: { createOverflowLink?: () => string }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n // Lazy: only mint a token if Slack actually rejects the message.\n let overflowLink: string | undefined;\n const resolveOverflowLink = (): string | undefined => {\n if (overflowLink === undefined && options?.createOverflowLink) {\n overflowLink = options.createOverflowLink();\n }\n return overflowLink;\n };\n\n const postOrUpdate = async (body: string): Promise<void> => {\n if (messageTs) {\n await slack.updateMessage(channelId, messageTs, body);\n return;\n }\n if (isThreaded && rootTs) {\n messageTs = await slack.postInThread(channelId, rootTs, body);\n return;\n }\n messageTs = await postFirstMessage(body);\n if (isSyntheticEvent && !event.thread_ts && messageTs) {\n slack.aliasSyntheticEventThread(channelId, messageTs, event.ts);\n }\n };\n\n accumulatedText = text;\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n\n try {\n await postOrUpdate(displayText);\n } catch (err) {\n if (!isSlackMsgTooLong(err)) throw err;\n const link = resolveOverflowLink();\n const fallback = await postSlackTextWithFallback(\n async (body) => {\n await postOrUpdate(body);\n },\n text,\n link,\n );\n accumulatedText = fallback.text;\n const continuation = text.slice(fallback.prefixLength).trimStart();\n if (continuation) {\n await postDiagnosticDirect(`_(continued from truncated message)_\\n\\n${continuation}`);\n }\n }\n } catch (err) {\n log.logWarning(\n \"Slack replaceResponse error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n await postDiagnosticDirect(text, options);\n } catch (err) {\n log.logWarning(\n \"Slack respondDiagnostic error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responseCtx.respondDiagnostic(formatSlackToolResult(result));\n },\n\n setTyping: async (isTyping: boolean) => {\n if (isTyping && !messageTs && rootTs) {\n try {\n const statusText = eventFilename ? `Starting event: ${eventFilename}` : \"Thinking\";\n await slack.setAssistantStatus(channelId, rootTs, statusText);\n } catch (err) {\n // Assistant API not available — first respond() call will create the message.\n onAssistantStatusError(\"typing\", err);\n }\n }\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await slack.uploadFile(channelId, filePath, title, rootTs);\n },\n\n setWorking: async (working: boolean) => {\n updatePromise = updatePromise.then(async () => {\n try {\n isWorking = working;\n if (messageTs) {\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n const updates: Promise<void>[] = [\n slack.updateMessage(channelId, messageTs, displayText),\n ];\n if (!working) {\n if (rootTs) {\n updates.push(\n slack\n .setAssistantStatus(channelId, rootTs, \"\")\n .catch((err) => onAssistantStatusError(\"clear-on-idle\", err)),\n );\n }\n }\n await Promise.all(updates);\n }\n } catch (err) {\n log.logWarning(\n \"Slack setWorking error\",\n err instanceof Error ? err.message : String(err),\n );\n }\n });\n await updatePromise;\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n // Clear assistant status first\n if (rootTs) {\n try {\n await slack.setAssistantStatus(channelId, rootTs, \"\");\n } catch {\n // Ignore errors clearing status\n }\n }\n\n // Delete thread messages first (in reverse order)\n for (let i = threadMessageTs.length - 1; i >= 0; i--) {\n try {\n await slack.deleteMessage(channelId, threadMessageTs[i]);\n } catch {\n // Ignore errors deleting thread messages\n }\n }\n threadMessageTs.length = 0;\n // Then delete main message\n if (messageTs) {\n await slack.deleteMessage(channelId, messageTs);\n messageTs = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/slack/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ChatMessage,\n ChatResponseContext,\n ChatToolResult,\n PlatformInfo,\n} from \"../../adapter.js\";\nimport * as log from \"../../log.js\";\nimport { createChatResponseErrorReporter, formatToolArgs, splitText } from \"../shared.js\";\nimport type { SlackBot, SlackEvent } from \"./bot.js\";\nimport { planSlackAdapterSession } from \"./session.js\";\n\nexport const SLACK_FORMATTING_GUIDE = `## Slack Formatting (mrkdwn, NOT Markdown)\nBold: *text*, Italic: _text_, Code: \\`code\\`, Block: \\`\\`\\`code\\`\\`\\`, Links: <url|text>\nDo NOT use **double asterisks** or [markdown](links).`;\n\nexport interface SlackAdapterOptions {\n initialMessageTs?: string;\n}\n\nconst MAX_MAIN_LENGTH = 35000; // Best-effort streaming cap; final responses use Slack error-driven fallback.\nconst MAX_THREAD_LENGTH = 20000;\nconst FALLBACK_MAIN_LENGTH = 3000;\nconst WORKING_INDICATOR = \" ...\";\nconst TRUNCATION_NOTE_INCREMENTAL =\n \"\\n\\n_(message truncated, ask me to elaborate on specific parts)_\";\n\nconst formatSlackContinuation = (partNum: number): string => `_(continued ${partNum})_`;\n\nfunction isSlackMsgTooLong(err: unknown): boolean {\n const data = (err as { data?: { error?: string } } | undefined)?.data;\n const message = err instanceof Error ? err.message : String(err);\n return data?.error === \"msg_too_long\" || message.includes(\"msg_too_long\");\n}\n\nfunction fallbackLongSlackText(\n text: string,\n overflowLink?: string,\n prefixLength = FALLBACK_MAIN_LENGTH,\n): string {\n const suffix = overflowLink\n ? `\\n\\n_(message too long for Slack; continued in thread; session view: <${overflowLink}|open>)_`\n : \"\\n\\n_(message too long for Slack; continued in thread)_\";\n return `${text.slice(0, prefixLength)}${suffix}`;\n}\n\nasync function postSlackTextWithFallback(\n post: (text: string) => Promise<string | void>,\n text: string,\n overflowLink?: string,\n): Promise<{ result: string | void; text: string; prefixLength: number }> {\n let prefixLength = FALLBACK_MAIN_LENGTH;\n let lastErr: unknown;\n\n for (;;) {\n const fallbackText = fallbackLongSlackText(text, overflowLink, prefixLength);\n try {\n const result = await post(fallbackText);\n return { result, text: fallbackText, prefixLength };\n } catch (err) {\n if (!isSlackMsgTooLong(err)) throw err;\n lastErr = err;\n if (prefixLength === 0) {\n throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));\n }\n prefixLength = Math.max(0, Math.floor(prefixLength / 2));\n }\n }\n}\n\nfunction formatSlackToolResult(result: ChatToolResult): string {\n const argsFormatted = formatToolArgs(result.args);\n const duration = (result.durationMs / 1000).toFixed(1);\n let text = `*${result.isError ? \"✗\" : \"✓\"} ${result.toolName}*`;\n if (result.label) text += `: ${result.label}`;\n text += ` (${duration}s)\\n`;\n if (argsFormatted) text += `\\`\\`\\`\\n${argsFormatted}\\n\\`\\`\\`\\n`;\n text += `*Result:*\\n\\`\\`\\`\\n${result.result}\\n\\`\\`\\``;\n return text;\n}\n\nexport function createSlackAdapters(\n event: SlackEvent,\n slack: SlackBot,\n adapterOptions: SlackAdapterOptions = {},\n): {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n} {\n const sessionPlan = planSlackAdapterSession(event, {\n initialMessageTs: adapterOptions.initialMessageTs,\n });\n let messageTs: string | null = sessionPlan.initialMessageTs ?? null;\n let assistantStatusFailureWarned = false;\n const onAssistantStatusError = (label: string, err: unknown): void => {\n if (assistantStatusFailureWarned) return;\n assistantStatusFailureWarned = true;\n log.logWarning(\n `Slack setAssistantStatus failed (${label}; further occurrences suppressed for this session)`,\n err instanceof Error ? err.message : String(err),\n );\n };\n const threadMessageTs: string[] = [];\n let accumulatedText = \"\";\n let isWorking = true;\n let updatePromise = Promise.resolve();\n\n const channelId = event.channel;\n const conversationId = event.conversationId;\n const user = slack.getUser(event.user);\n\n // Slack message timestamps are numeric; event-file triggers use `event:<filename>`.\n const eventFilename = event.ts.match(/^event:([^:]+(?:\\.json)?)/)?.[1];\n\n const { rootTs, isThreaded } = sessionPlan;\n\n /**\n * Post the first visible reply.\n * Default Slack behavior is now top-level channel replies.\n * If the triggering message is already inside a thread, stay in that thread.\n */\n const postFirstMessage = async (text: string): Promise<string> => {\n if (isThreaded && rootTs) {\n return slack.postInThread(channelId, rootTs, text);\n }\n return slack.postMessage(channelId, text);\n };\n\n const postDiagnosticDirect = async (\n text: string,\n options?: { style?: \"muted\" | \"error\" },\n ): Promise<void> => {\n const threadAnchor = messageTs ?? rootTs;\n if (!threadAnchor) return;\n\n for (const part of splitText(text, MAX_THREAD_LENGTH, formatSlackContinuation)) {\n if (options?.style === \"muted\") {\n const CONTEXT_TEXT_LIMIT = 3000;\n const blockText =\n part.length > CONTEXT_TEXT_LIMIT\n ? part.substring(0, CONTEXT_TEXT_LIMIT - 20) + \"\\n_(truncated)_\"\n : part;\n const ts = await slack.postInThreadBlocks(channelId, threadAnchor, part, [\n { type: \"context\", elements: [{ type: \"mrkdwn\", text: blockText }] },\n ]);\n threadMessageTs.push(ts);\n } else {\n const diagnosticText = options?.style === \"error\" ? `_${part}_` : part;\n const ts = await slack.postInThread(channelId, threadAnchor, diagnosticText);\n threadMessageTs.push(ts);\n }\n }\n };\n\n const message: ChatMessage = {\n id: event.ts,\n sessionKey: sessionPlan.sessionKey,\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: user?.userName,\n text: event.text,\n attachments: (event.attachments || []).map((a) => ({\n name: a.original,\n localPath: a.localPath,\n })),\n threadTs: event.thread_ts,\n };\n\n const platform: PlatformInfo = {\n name: \"slack\",\n formattingGuide: SLACK_FORMATTING_GUIDE,\n channels: slack.getAllChannels().map((c) => ({ id: c.id, name: c.name })),\n users: slack\n .getAllUsers()\n .map((u) => ({ id: u.id, userName: u.userName, displayName: u.displayName })),\n diagnostics: {\n showUsageSummary: true,\n },\n };\n\n const reportResponseError = createChatResponseErrorReporter(() => ({\n platform: \"slack\",\n conversationId,\n channelId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: messageTs,\n threadTs: rootTs,\n conversationKind: message.conversationKind,\n isThreaded,\n }));\n\n const responseCtx = {\n respond: async (text: string) => {\n updatePromise = updatePromise.then(async () => {\n try {\n accumulatedText = accumulatedText ? `${accumulatedText}\\n${text}` : text;\n\n const mainLimit = isWorking\n ? MAX_MAIN_LENGTH - WORKING_INDICATOR.length\n : MAX_MAIN_LENGTH;\n if (accumulatedText.length > mainLimit) {\n accumulatedText =\n accumulatedText.substring(0, mainLimit - TRUNCATION_NOTE_INCREMENTAL.length) +\n TRUNCATION_NOTE_INCREMENTAL;\n }\n\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n\n if (messageTs) {\n await slack.updateMessage(channelId, messageTs, displayText);\n } else if (isThreaded && rootTs) {\n // Reply within the user's thread\n messageTs = await slack.postInThread(channelId, rootTs, displayText);\n } else {\n messageTs = await postFirstMessage(displayText);\n }\n\n if (messageTs) {\n slack.logBotResponse(channelId, text, messageTs, isThreaded ? rootTs : undefined);\n }\n } catch (err) {\n log.logWarning(\"Slack respond error\", err instanceof Error ? err.message : String(err));\n reportResponseError(err, \"respond\", {\n phase: messageTs ? \"update\" : \"initial_post\",\n textLength: text.length,\n accumulatedLength: accumulatedText.length,\n });\n }\n });\n await updatePromise;\n },\n\n replaceResponse: async (text: string, options?: { createOverflowLink?: () => string }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n // Lazy: only mint a token if Slack actually rejects the message.\n let overflowLink: string | undefined;\n const resolveOverflowLink = (): string | undefined => {\n if (overflowLink === undefined && options?.createOverflowLink) {\n overflowLink = options.createOverflowLink();\n }\n return overflowLink;\n };\n\n const postOrUpdate = async (body: string): Promise<void> => {\n if (messageTs) {\n await slack.updateMessage(channelId, messageTs, body);\n return;\n }\n if (isThreaded && rootTs) {\n messageTs = await slack.postInThread(channelId, rootTs, body);\n return;\n }\n messageTs = await postFirstMessage(body);\n };\n\n accumulatedText = text;\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n\n try {\n await postOrUpdate(displayText);\n } catch (err) {\n if (!isSlackMsgTooLong(err)) throw err;\n const link = resolveOverflowLink();\n const fallback = await postSlackTextWithFallback(\n async (body) => {\n await postOrUpdate(body);\n },\n text,\n link,\n );\n accumulatedText = fallback.text;\n const continuation = text.slice(fallback.prefixLength).trimStart();\n if (continuation) {\n await postDiagnosticDirect(`_(continued from truncated message)_\\n\\n${continuation}`);\n }\n }\n } catch (err) {\n log.logWarning(\n \"Slack replaceResponse error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"replace_response\", {\n textLength: text.length,\n hadExistingResponse: Boolean(messageTs),\n });\n }\n });\n await updatePromise;\n },\n\n respondDiagnostic: async (text: string, options?: { style?: \"muted\" | \"error\" }) => {\n updatePromise = updatePromise.then(async () => {\n try {\n await postDiagnosticDirect(text, options);\n } catch (err) {\n log.logWarning(\n \"Slack respondDiagnostic error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"respond_diagnostic\", {\n textLength: text.length,\n style: options?.style,\n });\n }\n });\n await updatePromise;\n },\n\n respondToolResult: async (result: ChatToolResult) => {\n await responseCtx.respondDiagnostic(formatSlackToolResult(result));\n },\n\n setTyping: async (isTyping: boolean) => {\n if (isTyping && !messageTs && rootTs) {\n try {\n const statusText = eventFilename ? `Starting event: ${eventFilename}` : \"Thinking\";\n await slack.setAssistantStatus(channelId, rootTs, statusText);\n } catch (err) {\n // Assistant API not available — first respond() call will create the message.\n onAssistantStatusError(\"typing\", err);\n }\n }\n },\n\n uploadFile: async (filePath: string, title?: string) => {\n await slack.uploadFile(channelId, filePath, title, rootTs);\n },\n\n setWorking: async (working: boolean) => {\n updatePromise = updatePromise.then(async () => {\n try {\n isWorking = working;\n if (messageTs) {\n const displayText = isWorking ? accumulatedText + WORKING_INDICATOR : accumulatedText;\n const updates: Promise<void>[] = [\n slack.updateMessage(channelId, messageTs, displayText),\n ];\n if (!working) {\n if (rootTs) {\n updates.push(\n slack\n .setAssistantStatus(channelId, rootTs, \"\")\n .catch((err) => onAssistantStatusError(\"clear-on-idle\", err)),\n );\n }\n }\n await Promise.all(updates);\n }\n } catch (err) {\n log.logWarning(\n \"Slack setWorking error\",\n err instanceof Error ? err.message : String(err),\n );\n reportResponseError(err, \"set_working\", { working });\n }\n });\n await updatePromise;\n },\n\n deleteResponse: async () => {\n updatePromise = updatePromise.then(async () => {\n // Clear assistant status first\n if (rootTs) {\n try {\n await slack.setAssistantStatus(channelId, rootTs, \"\");\n } catch {\n // Ignore errors clearing status\n }\n }\n\n // Delete thread messages first (in reverse order)\n for (let i = threadMessageTs.length - 1; i >= 0; i--) {\n try {\n await slack.deleteMessage(channelId, threadMessageTs[i]);\n } catch {\n // Ignore errors deleting thread messages\n }\n }\n threadMessageTs.length = 0;\n // Then delete main message\n if (messageTs) {\n await slack.deleteMessage(channelId, messageTs);\n messageTs = null;\n }\n });\n await updatePromise;\n },\n };\n\n return { message, responseCtx, platform };\n}\n"]}
|
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
interface SlackSessionEventLike {
|
|
2
|
+
conversationId: string;
|
|
3
|
+
ts: string;
|
|
4
|
+
thread_ts?: string;
|
|
5
|
+
sessionKey?: string;
|
|
6
|
+
}
|
|
7
|
+
export type SlackSessionRef = {
|
|
8
|
+
kind: "channel";
|
|
9
|
+
channelId: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "fork";
|
|
12
|
+
channelId: string;
|
|
13
|
+
anchorTs: string;
|
|
14
|
+
};
|
|
15
|
+
export interface SlackAdapterSessionPlan {
|
|
16
|
+
sessionKey: string;
|
|
17
|
+
rootTs?: string;
|
|
18
|
+
initialMessageTs?: string;
|
|
19
|
+
isThreaded: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface SlackEventForkRunPlan<T extends SlackSessionEventLike> {
|
|
22
|
+
event: T;
|
|
23
|
+
initialMessageTs?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function formatSlackSessionKey(ref: SlackSessionRef): string;
|
|
26
|
+
export declare function parseSlackSessionKey(sessionKey: string): SlackSessionRef;
|
|
27
|
+
export declare function isSlackForkSessionKey(sessionKey: string): boolean;
|
|
28
|
+
export declare function resolveSlackSessionRef(channelId: string, threadTs?: string): SlackSessionRef;
|
|
1
29
|
export declare function resolveSlackSessionKey(channelId: string, threadTs?: string): string;
|
|
2
30
|
export declare function resolveSlackRootTs(messageTs: string, threadTs?: string): string;
|
|
31
|
+
export declare function isSlackMessageTs(ts: string | undefined): ts is string;
|
|
32
|
+
export declare function resolveSlackResponseRootTs(event: Pick<SlackSessionEventLike, "ts" | "thread_ts">): string | undefined;
|
|
33
|
+
export declare function planSlackAdapterSession(event: SlackSessionEventLike, options?: {
|
|
34
|
+
initialMessageTs?: string;
|
|
35
|
+
}): SlackAdapterSessionPlan;
|
|
36
|
+
export declare function planSlackEventForkRun<T extends SlackSessionEventLike>(event: T, anchorTs?: string): SlackEventForkRunPlan<T>;
|
|
37
|
+
export {};
|
|
3
38
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/session.ts"],"names":[],"mappings":"AAGA,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/session.ts"],"names":[],"mappings":"AAGA,UAAU,qBAAqB;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,qBAAqB;IACpE,KAAK,EAAE,CAAC,CAAC;IACT,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAElE;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,CAUxE;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,eAAe,CAI5F;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAWnF;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,EAAE,IAAI,MAAM,CAErE;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,IAAI,GAAG,WAAW,CAAC,GACrD,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1C,uBAAuB,CAUzB;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,qBAAqB,EACnE,KAAK,EAAE,CAAC,EACR,QAAQ,CAAC,EAAE,MAAM,GAChB,qBAAqB,CAAC,CAAC,CAAC,CAY1B","sourcesContent":["import type { ConversationKind } from \"../../adapter.js\";\nimport { resolveChatSessionKey } from \"../../session-policy.js\";\n\ninterface SlackSessionEventLike {\n conversationId: string;\n ts: string;\n thread_ts?: string;\n sessionKey?: string;\n}\n\nexport type SlackSessionRef =\n | { kind: \"channel\"; channelId: string }\n | { kind: \"fork\"; channelId: string; anchorTs: string };\n\nexport interface SlackAdapterSessionPlan {\n sessionKey: string;\n rootTs?: string;\n initialMessageTs?: string;\n isThreaded: boolean;\n}\n\nexport interface SlackEventForkRunPlan<T extends SlackSessionEventLike> {\n event: T;\n initialMessageTs?: string;\n}\n\nexport function formatSlackSessionKey(ref: SlackSessionRef): string {\n return ref.kind === \"channel\" ? ref.channelId : `${ref.channelId}:${ref.anchorTs}`;\n}\n\nexport function parseSlackSessionKey(sessionKey: string): SlackSessionRef {\n const separator = sessionKey.indexOf(\":\");\n if (separator === -1) {\n return { kind: \"channel\", channelId: sessionKey };\n }\n return {\n kind: \"fork\",\n channelId: sessionKey.slice(0, separator),\n anchorTs: sessionKey.slice(separator + 1),\n };\n}\n\nexport function isSlackForkSessionKey(sessionKey: string): boolean {\n return parseSlackSessionKey(sessionKey).kind === \"fork\";\n}\n\nexport function resolveSlackSessionRef(channelId: string, threadTs?: string): SlackSessionRef {\n return threadTs\n ? { kind: \"fork\", channelId, anchorTs: threadTs }\n : { kind: \"channel\", channelId };\n}\n\nexport function resolveSlackSessionKey(channelId: string, threadTs?: string): string {\n const conversationKind: ConversationKind = channelId.startsWith(\"D\") ? \"direct\" : \"shared\";\n const sessionKey = resolveChatSessionKey({\n conversationId: channelId,\n conversationKind,\n messageId: channelId,\n threadTs,\n persistentTopLevel: true,\n scopeDirectThreads: true,\n });\n return formatSlackSessionKey(parseSlackSessionKey(sessionKey));\n}\n\nexport function resolveSlackRootTs(messageTs: string, threadTs?: string): string {\n return threadTs || messageTs;\n}\n\nexport function isSlackMessageTs(ts: string | undefined): ts is string {\n return typeof ts === \"string\" && /^\\d+\\.\\d+$/.test(ts);\n}\n\nexport function resolveSlackResponseRootTs(\n event: Pick<SlackSessionEventLike, \"ts\" | \"thread_ts\">,\n): string | undefined {\n return event.thread_ts ?? (isSlackMessageTs(event.ts) ? resolveSlackRootTs(event.ts) : undefined);\n}\n\nexport function planSlackAdapterSession(\n event: SlackSessionEventLike,\n options: { initialMessageTs?: string } = {},\n): SlackAdapterSessionPlan {\n const sessionKey =\n event.sessionKey ?? resolveSlackSessionKey(event.conversationId, event.thread_ts);\n\n return {\n sessionKey,\n rootTs: options.initialMessageTs ?? resolveSlackResponseRootTs(event),\n initialMessageTs: options.initialMessageTs,\n isThreaded: !!event.thread_ts,\n };\n}\n\nexport function planSlackEventForkRun<T extends SlackSessionEventLike>(\n event: T,\n anchorTs?: string,\n): SlackEventForkRunPlan<T> {\n if (!anchorTs || event.thread_ts) {\n return { event };\n }\n\n return {\n event: {\n ...event,\n sessionKey: resolveSlackSessionKey(event.conversationId, anchorTs),\n },\n initialMessageTs: anchorTs,\n };\n}\n"]}
|
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import { resolveChatSessionKey } from "../../session-policy.js";
|
|
2
|
+
export function formatSlackSessionKey(ref) {
|
|
3
|
+
return ref.kind === "channel" ? ref.channelId : `${ref.channelId}:${ref.anchorTs}`;
|
|
4
|
+
}
|
|
5
|
+
export function parseSlackSessionKey(sessionKey) {
|
|
6
|
+
const separator = sessionKey.indexOf(":");
|
|
7
|
+
if (separator === -1) {
|
|
8
|
+
return { kind: "channel", channelId: sessionKey };
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
kind: "fork",
|
|
12
|
+
channelId: sessionKey.slice(0, separator),
|
|
13
|
+
anchorTs: sessionKey.slice(separator + 1),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function isSlackForkSessionKey(sessionKey) {
|
|
17
|
+
return parseSlackSessionKey(sessionKey).kind === "fork";
|
|
18
|
+
}
|
|
19
|
+
export function resolveSlackSessionRef(channelId, threadTs) {
|
|
20
|
+
return threadTs
|
|
21
|
+
? { kind: "fork", channelId, anchorTs: threadTs }
|
|
22
|
+
: { kind: "channel", channelId };
|
|
23
|
+
}
|
|
2
24
|
export function resolveSlackSessionKey(channelId, threadTs) {
|
|
3
25
|
const conversationKind = channelId.startsWith("D") ? "direct" : "shared";
|
|
4
|
-
|
|
26
|
+
const sessionKey = resolveChatSessionKey({
|
|
5
27
|
conversationId: channelId,
|
|
6
28
|
conversationKind,
|
|
7
29
|
messageId: channelId,
|
|
@@ -9,8 +31,36 @@ export function resolveSlackSessionKey(channelId, threadTs) {
|
|
|
9
31
|
persistentTopLevel: true,
|
|
10
32
|
scopeDirectThreads: true,
|
|
11
33
|
});
|
|
34
|
+
return formatSlackSessionKey(parseSlackSessionKey(sessionKey));
|
|
12
35
|
}
|
|
13
36
|
export function resolveSlackRootTs(messageTs, threadTs) {
|
|
14
37
|
return threadTs || messageTs;
|
|
15
38
|
}
|
|
39
|
+
export function isSlackMessageTs(ts) {
|
|
40
|
+
return typeof ts === "string" && /^\d+\.\d+$/.test(ts);
|
|
41
|
+
}
|
|
42
|
+
export function resolveSlackResponseRootTs(event) {
|
|
43
|
+
return event.thread_ts ?? (isSlackMessageTs(event.ts) ? resolveSlackRootTs(event.ts) : undefined);
|
|
44
|
+
}
|
|
45
|
+
export function planSlackAdapterSession(event, options = {}) {
|
|
46
|
+
const sessionKey = event.sessionKey ?? resolveSlackSessionKey(event.conversationId, event.thread_ts);
|
|
47
|
+
return {
|
|
48
|
+
sessionKey,
|
|
49
|
+
rootTs: options.initialMessageTs ?? resolveSlackResponseRootTs(event),
|
|
50
|
+
initialMessageTs: options.initialMessageTs,
|
|
51
|
+
isThreaded: !!event.thread_ts,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function planSlackEventForkRun(event, anchorTs) {
|
|
55
|
+
if (!anchorTs || event.thread_ts) {
|
|
56
|
+
return { event };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
event: {
|
|
60
|
+
...event,
|
|
61
|
+
sessionKey: resolveSlackSessionKey(event.conversationId, anchorTs),
|
|
62
|
+
},
|
|
63
|
+
initialMessageTs: anchorTs,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
16
66
|
//# sourceMappingURL=session.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/adapters/slack/session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/adapters/slack/session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConversationKind } from \"../../adapter.js\";\nimport { resolveChatSessionKey } from \"../../session-policy.js\";\n\ninterface SlackSessionEventLike {\n conversationId: string;\n ts: string;\n thread_ts?: string;\n sessionKey?: string;\n}\n\nexport type SlackSessionRef =\n | { kind: \"channel\"; channelId: string }\n | { kind: \"fork\"; channelId: string; anchorTs: string };\n\nexport interface SlackAdapterSessionPlan {\n sessionKey: string;\n rootTs?: string;\n initialMessageTs?: string;\n isThreaded: boolean;\n}\n\nexport interface SlackEventForkRunPlan<T extends SlackSessionEventLike> {\n event: T;\n initialMessageTs?: string;\n}\n\nexport function formatSlackSessionKey(ref: SlackSessionRef): string {\n return ref.kind === \"channel\" ? ref.channelId : `${ref.channelId}:${ref.anchorTs}`;\n}\n\nexport function parseSlackSessionKey(sessionKey: string): SlackSessionRef {\n const separator = sessionKey.indexOf(\":\");\n if (separator === -1) {\n return { kind: \"channel\", channelId: sessionKey };\n }\n return {\n kind: \"fork\",\n channelId: sessionKey.slice(0, separator),\n anchorTs: sessionKey.slice(separator + 1),\n };\n}\n\nexport function isSlackForkSessionKey(sessionKey: string): boolean {\n return parseSlackSessionKey(sessionKey).kind === \"fork\";\n}\n\nexport function resolveSlackSessionRef(channelId: string, threadTs?: string): SlackSessionRef {\n return threadTs\n ? { kind: \"fork\", channelId, anchorTs: threadTs }\n : { kind: \"channel\", channelId };\n}\n\nexport function resolveSlackSessionKey(channelId: string, threadTs?: string): string {\n const conversationKind: ConversationKind = channelId.startsWith(\"D\") ? \"direct\" : \"shared\";\n const sessionKey = resolveChatSessionKey({\n conversationId: channelId,\n conversationKind,\n messageId: channelId,\n threadTs,\n persistentTopLevel: true,\n scopeDirectThreads: true,\n });\n return formatSlackSessionKey(parseSlackSessionKey(sessionKey));\n}\n\nexport function resolveSlackRootTs(messageTs: string, threadTs?: string): string {\n return threadTs || messageTs;\n}\n\nexport function isSlackMessageTs(ts: string | undefined): ts is string {\n return typeof ts === \"string\" && /^\\d+\\.\\d+$/.test(ts);\n}\n\nexport function resolveSlackResponseRootTs(\n event: Pick<SlackSessionEventLike, \"ts\" | \"thread_ts\">,\n): string | undefined {\n return event.thread_ts ?? (isSlackMessageTs(event.ts) ? resolveSlackRootTs(event.ts) : undefined);\n}\n\nexport function planSlackAdapterSession(\n event: SlackSessionEventLike,\n options: { initialMessageTs?: string } = {},\n): SlackAdapterSessionPlan {\n const sessionKey =\n event.sessionKey ?? resolveSlackSessionKey(event.conversationId, event.thread_ts);\n\n return {\n sessionKey,\n rootTs: options.initialMessageTs ?? resolveSlackResponseRootTs(event),\n initialMessageTs: options.initialMessageTs,\n isThreaded: !!event.thread_ts,\n };\n}\n\nexport function planSlackEventForkRun<T extends SlackSessionEventLike>(\n event: T,\n anchorTs?: string,\n): SlackEventForkRunPlan<T> {\n if (!anchorTs || event.thread_ts) {\n return { event };\n }\n\n return {\n event: {\n ...event,\n sessionKey: resolveSlackSessionKey(event.conversationId, anchorTs),\n },\n initialMessageTs: anchorTs,\n };\n}\n"]}
|