@jobmatchme/bee-slack 0.1.8 → 0.1.9

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.
@@ -20,4 +20,5 @@ export interface SlackScheduledRunConfig {
20
20
  export declare function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig;
21
21
  export declare function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void>;
22
22
  export declare function runScheduledSlackTurn(gatewayConfig: SlackGatewayConfig, scheduledRun: SlackScheduledRunConfig): Promise<void>;
23
+ export declare function renderScheduledFinalMessage(requestText: string | undefined, responseText: string): string;
23
24
  //# sourceMappingURL=scheduled.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../src/scheduled.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,KAAK,CAAC,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA8BD,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,uBAAuB,CAYhF;AAED,wBAAsB,8BAA8B,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzG;AAED,wBAAsB,qBAAqB,CAC1C,aAAa,EAAE,kBAAkB,EACjC,YAAY,EAAE,uBAAuB,GACnC,OAAO,CAAC,IAAI,CAAC,CAqEf","sourcesContent":["import {\n\ttype ArtifactRef,\n\ttype BeeResolvedTurn,\n\ttype BeeRunEvent,\n\ttype BeeWorkerClient,\n\tbuildConversationId,\n\tbuildSessionKey,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\tnewTurnId,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { WebClient } from \"@slack/web-api\";\nimport { readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport * as log from \"./log.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport type { SlackGatewayConfig } from \"./types.js\";\n\nexport interface SlackScheduledRunConfig {\n\tid: string;\n\trouteId: string;\n\ttext: string;\n\ttarget: {\n\t\tslackUserId?: string;\n\t\tchannelId?: string;\n\t\tthreadTs?: string;\n\t};\n\tactor?: {\n\t\tuserId: string;\n\t\tuserName?: string;\n\t\tdisplayName?: string;\n\t};\n\tsessionId?: string;\n\tsessionPrefix?: string;\n\tconversationId?: string;\n}\n\ninterface RenderState {\n\tstatusRef?: string;\n\tlatestText: string;\n\titemTexts: Map<string, string>;\n}\n\ntype ItemPartLike = { kind: string; [key: string]: unknown };\n\ntype ItemAppendedPayload = {\n\teventType: \"item.appended\";\n\titem: { id: string; kind: string; role: string; parts: ItemPartLike[] };\n};\n\ntype ItemUpdatedPayload = {\n\teventType: \"item.updated\";\n\titemId: string;\n\tappendParts?: ItemPartLike[];\n};\n\ntype RunFailedPayload = {\n\teventType: \"run.failed\";\n\terror: string;\n};\n\ntype ApprovalRequestedPayload = {\n\tsummary: string;\n};\n\nexport function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig {\n\tconst path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;\n\tif (!path) {\n\t\tthrow new Error(\n\t\t\t\"Missing scheduled run config path; pass it as second argument or set BEE_SLACK_SCHEDULED_RUN_CONFIG\",\n\t\t);\n\t}\n\n\tconst fullPath = resolve(path);\n\tconst config = JSON.parse(readFileSync(fullPath, \"utf-8\")) as SlackScheduledRunConfig;\n\tvalidateScheduledRunConfig(config, fullPath);\n\treturn config;\n}\n\nexport async function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void> {\n\tconst gatewayConfig = loadConfig(configPath);\n\tconst scheduledRun = loadScheduledRunConfig(jobPath);\n\tawait runScheduledSlackTurn(gatewayConfig, scheduledRun);\n}\n\nexport async function runScheduledSlackTurn(\n\tgatewayConfig: SlackGatewayConfig,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<void> {\n\tvalidateScheduledRunConfig(scheduledRun, \"scheduled run config\");\n\tconst route = gatewayConfig.routes.find((candidate) => candidate.id === scheduledRun.routeId);\n\tif (!route) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} references unknown routeId ${scheduledRun.routeId}`);\n\t}\n\n\tconst webClient = new WebClient(gatewayConfig.botToken);\n\tconst blobStore = new LocalFileBlobStore(\n\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t);\n\tconst sink = new SlackSink(webClient, blobStore);\n\tconst workerClient = await createNatsBeeClient(gatewayConfig.nats);\n\n\ttry {\n\t\tconst auth = await webClient.auth.test();\n\t\tconst teamId = String(auth.team_id || \"unknown-team\");\n\t\tconst output = await resolveScheduledOutputTarget(webClient, scheduledRun);\n\t\tconst actor = await resolveScheduledActor(webClient, scheduledRun);\n\t\tif (!output.threadId) {\n\t\t\toutput.threadId = await sink.postMessage(\n\t\t\t\t{ channelId: output.channelId },\n\t\t\t\t`:alarm_clock: Scheduled Bee run \\`${scheduledRun.id}\\` started. Reply in this thread to continue the session.`,\n\t\t\t);\n\t\t}\n\t\tconst slackConversationId = buildConversationId([\n\t\t\t\"slack\",\n\t\t\tteamId,\n\t\t\toutput.channelId || \"unknown-channel\",\n\t\t\toutput.threadId,\n\t\t]);\n\t\tconst conversationId = scheduledRun.conversationId || slackConversationId;\n\t\tconst sessionBase =\n\t\t\troute.session?.strategy === \"channel\" ? output.channelId || \"unknown-channel\" : output.threadId;\n\t\tconst routeSessionId = buildSessionKey(\n\t\t\tscheduledRun.sessionPrefix || route.session?.prefix || route.id,\n\t\t\tbuildConversationId([\"slack\", teamId, output.channelId || \"unknown-channel\", sessionBase || output.threadId]),\n\t\t);\n\t\tconst sessionId = scheduledRun.sessionId || routeSessionId;\n\n\t\tconst input: BeeResolvedTurn = {\n\t\t\tsessionId,\n\t\t\tthreadId: output.threadId,\n\t\t\tworker: route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId,\n\t\t\t},\n\t\t\tactor,\n\t\t\tmessage: {\n\t\t\t\ttext: scheduledRun.text,\n\t\t\t},\n\t\t\tattachments: [],\n\t\t\toutput,\n\t\t};\n\n\t\tlog.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);\n\t\tawait streamScheduledTurn(workerClient, sink, input);\n\t\tlog.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog.logError(`Scheduled Slack run ${scheduledRun.id} failed`, message);\n\t\tthrow error;\n\t} finally {\n\t\tawait workerClient.close?.();\n\t}\n}\n\nasync function resolveScheduledOutputTarget(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<TransportOutputTarget> {\n\tif (scheduledRun.target.channelId) {\n\t\treturn {\n\t\t\tchannelId: scheduledRun.target.channelId,\n\t\t\tthreadId: scheduledRun.target.threadTs,\n\t\t};\n\t}\n\n\tif (!scheduledRun.target.slackUserId) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} needs target.slackUserId or target.channelId`);\n\t}\n\n\tconst result = await webClient.conversations.open({ users: scheduledRun.target.slackUserId });\n\tconst channel = result.channel as { id?: string } | undefined;\n\tif (!channel?.id) {\n\t\tthrow new Error(`Slack conversations.open did not return a channel for user ${scheduledRun.target.slackUserId}`);\n\t}\n\treturn {\n\t\tchannelId: channel.id,\n\t\tthreadId: scheduledRun.target.threadTs,\n\t};\n}\n\nasync function resolveScheduledActor(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<BeeResolvedTurn[\"actor\"]> {\n\tif (scheduledRun.actor) return scheduledRun.actor;\n\tif (!scheduledRun.target.slackUserId) {\n\t\treturn {\n\t\t\tuserId: `scheduler:${scheduledRun.id}`,\n\t\t\tdisplayName: \"Bee Scheduler\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst info = await webClient.users.info({ user: scheduledRun.target.slackUserId });\n\t\tconst user = info.user as\n\t\t\t| { id?: string; name?: string; real_name?: string; profile?: { display_name?: string } }\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tuserName: user?.name,\n\t\t\tdisplayName: user?.profile?.display_name || user?.real_name || user?.name,\n\t\t};\n\t} catch (error) {\n\t\tlog.logWarning(\n\t\t\t`Could not resolve Slack user ${scheduledRun.target.slackUserId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tdisplayName: scheduledRun.target.slackUserId,\n\t\t};\n\t}\n}\n\nasync function streamScheduledTurn(\n\tworkerClient: BeeWorkerClient,\n\tsink: SlackSink,\n\tinput: BeeResolvedTurn,\n): Promise<void> {\n\tconst state: RenderState = {\n\t\tlatestText: \"_Working..._\",\n\t\titemTexts: new Map<string, string>(),\n\t};\n\tconst request = {\n\t\tsessionId: input.sessionId,\n\t\tthreadId: input.threadId,\n\t\tturnId: newTurnId(),\n\t\tconversation: input.conversation,\n\t\tactor: input.actor,\n\t\tmessage: input.message,\n\t\tattachments: input.attachments,\n\t};\n\n\ttry {\n\t\tawait workerClient.streamTurn(input.worker, request, async (event) => {\n\t\t\tawait handleScheduledEvent(sink, input.output, event, state);\n\t\t});\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait sink.postMessage(input.output, `_Scheduled gateway error: ${message}_`);\n\t\tthrow error;\n\t}\n}\n\nasync function handleScheduledEvent(\n\tsink: SlackSink,\n\toutput: TransportOutputTarget,\n\tevent: BeeRunEvent,\n\tstate: RenderState,\n): Promise<void> {\n\tif (event.name === \"run.started\") {\n\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.completed\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.failed\") {\n\t\tconst errorText = `_Error: ${asRunFailedPayload(event).error}_`;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, errorText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, errorText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"approval.requested\") {\n\t\tawait sink.postMessage(output, `Approval requested: ${asApprovalRequestedPayload(event).summary}`);\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.appended\") {\n\t\tconst payload = asItemAppendedPayload(event);\n\t\tconst text = renderParts(payload.item.parts);\n\t\tstate.itemTexts.set(payload.item.id, text);\n\t\tif (payload.item.kind === \"artifact\") {\n\t\t\tconst artifact = firstArtifactRef(payload.item.parts);\n\t\t\tif (artifact && artifactHasPayload(artifact)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sink.publishArtifact(output, artifact);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tawait sink.postMessage(output, `${text}\\n_Artifact upload failed: ${message}_`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait sink.postMessage(output, text);\n\t\t\treturn;\n\t\t}\n\t\tstate.latestText = text;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, text);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, text);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.updated\") {\n\t\tconst payload = asItemUpdatedPayload(event);\n\t\tconst current = state.itemTexts.get(payload.itemId) || \"\";\n\t\tconst appended = renderParts(payload.appendParts || []);\n\t\tconst next = current ? `${current}${appended}` : appended;\n\t\tstate.itemTexts.set(payload.itemId, next);\n\t\tstate.latestText = next;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, next);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, next);\n\t\t}\n\t}\n}\n\nfunction firstArtifactRef(parts: ItemPartLike[]): ArtifactRef | undefined {\n\tconst part = parts.find((entry) => entry.kind === \"artifactRef\");\n\tif (!part) return undefined;\n\treturn {\n\t\tartifactId: String(part.artifactId || \"artifact\"),\n\t\tblobKey: typeof part.blobKey === \"string\" ? part.blobKey : undefined,\n\t\tname: typeof part.name === \"string\" ? part.name : undefined,\n\t\ttitle: typeof part.title === \"string\" ? part.title : undefined,\n\t\tmimeType: typeof part.mimeType === \"string\" ? part.mimeType : undefined,\n\t\turi: typeof part.uri === \"string\" ? part.uri : undefined,\n\t\tsizeBytes: typeof part.sizeBytes === \"number\" ? part.sizeBytes : undefined,\n\t};\n}\n\nfunction artifactHasPayload(artifact: ArtifactRef): boolean {\n\treturn !!(artifact.uri || artifact.blobKey);\n}\n\nfunction renderParts(parts: ItemPartLike[]): string {\n\treturn parts\n\t\t.map((part) => {\n\t\t\tif (part.kind === \"text\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"status\") return String(part.status || \"\");\n\t\t\tif (part.kind === \"artifactRef\") {\n\t\t\t\treturn `Artifact: ${String(part.title || part.name || part.artifactId || \"artifact\")}`;\n\t\t\t}\n\t\t\tif (part.kind === \"approval\" || part.kind === \"choice\") {\n\t\t\t\treturn `${String(part.title || \"\")}\\n${String(part.summary || \"\")}`.trim();\n\t\t\t}\n\t\t\tif (part.kind === \"form\") return String(part.title || \"\");\n\t\t\tif (part.kind === \"log\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"patch\" || part.kind === \"diff\") {\n\t\t\t\tconst files = Array.isArray(part.files) ? (part.files as Array<{ path?: unknown }>) : [];\n\t\t\t\treturn files.map((entry) => `File: ${String(entry.path || \"\")}`).join(\"\\n\");\n\t\t\t}\n\t\t\treturn JSON.stringify(part);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction validateScheduledRunConfig(config: SlackScheduledRunConfig, source: string): void {\n\tif (!config.id) throw new Error(`Missing id in ${source}`);\n\tif (!config.routeId) throw new Error(`Missing routeId in ${source}`);\n\tif (!config.text) throw new Error(`Missing text in ${source}`);\n\tif (!config.target) throw new Error(`Missing target in ${source}`);\n\tif (!config.target.slackUserId && !config.target.channelId) {\n\t\tthrow new Error(`Missing target.slackUserId or target.channelId in ${source}`);\n\t}\n}\n\nfunction asRunFailedPayload(event: BeeRunEvent): RunFailedPayload {\n\treturn event.payload as RunFailedPayload;\n}\n\nfunction asApprovalRequestedPayload(event: BeeRunEvent): ApprovalRequestedPayload {\n\treturn event.payload as ApprovalRequestedPayload;\n}\n\nfunction asItemAppendedPayload(event: BeeRunEvent): ItemAppendedPayload {\n\treturn event.payload as ItemAppendedPayload;\n}\n\nfunction asItemUpdatedPayload(event: BeeRunEvent): ItemUpdatedPayload {\n\treturn event.payload as ItemUpdatedPayload;\n}\n"]}
1
+ {"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../src/scheduled.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,KAAK,CAAC,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA+BD,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,uBAAuB,CAYhF;AAED,wBAAsB,8BAA8B,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzG;AAED,wBAAsB,qBAAqB,CAC1C,aAAa,EAAE,kBAAkB,EACjC,YAAY,EAAE,uBAAuB,GACnC,OAAO,CAAC,IAAI,CAAC,CAoEf;AA8LD,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAUzG","sourcesContent":["import {\n\ttype ArtifactRef,\n\ttype BeeResolvedTurn,\n\ttype BeeRunEvent,\n\ttype BeeWorkerClient,\n\tbuildConversationId,\n\tbuildSessionKey,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\tnewTurnId,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { WebClient } from \"@slack/web-api\";\nimport { readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport * as log from \"./log.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport type { SlackGatewayConfig } from \"./types.js\";\n\nexport interface SlackScheduledRunConfig {\n\tid: string;\n\trouteId: string;\n\ttext: string;\n\ttarget: {\n\t\tslackUserId?: string;\n\t\tchannelId?: string;\n\t\tthreadTs?: string;\n\t};\n\tactor?: {\n\t\tuserId: string;\n\t\tuserName?: string;\n\t\tdisplayName?: string;\n\t};\n\tsessionId?: string;\n\tsessionPrefix?: string;\n\tconversationId?: string;\n}\n\ninterface RenderState {\n\tstatusRef?: string;\n\tlatestText: string;\n\trequestText?: string;\n\titemTexts: Map<string, string>;\n}\n\ntype ItemPartLike = { kind: string; [key: string]: unknown };\n\ntype ItemAppendedPayload = {\n\teventType: \"item.appended\";\n\titem: { id: string; kind: string; role: string; parts: ItemPartLike[] };\n};\n\ntype ItemUpdatedPayload = {\n\teventType: \"item.updated\";\n\titemId: string;\n\tappendParts?: ItemPartLike[];\n};\n\ntype RunFailedPayload = {\n\teventType: \"run.failed\";\n\terror: string;\n};\n\ntype ApprovalRequestedPayload = {\n\tsummary: string;\n};\n\nexport function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig {\n\tconst path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;\n\tif (!path) {\n\t\tthrow new Error(\n\t\t\t\"Missing scheduled run config path; pass it as second argument or set BEE_SLACK_SCHEDULED_RUN_CONFIG\",\n\t\t);\n\t}\n\n\tconst fullPath = resolve(path);\n\tconst config = JSON.parse(readFileSync(fullPath, \"utf-8\")) as SlackScheduledRunConfig;\n\tvalidateScheduledRunConfig(config, fullPath);\n\treturn config;\n}\n\nexport async function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void> {\n\tconst gatewayConfig = loadConfig(configPath);\n\tconst scheduledRun = loadScheduledRunConfig(jobPath);\n\tawait runScheduledSlackTurn(gatewayConfig, scheduledRun);\n}\n\nexport async function runScheduledSlackTurn(\n\tgatewayConfig: SlackGatewayConfig,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<void> {\n\tvalidateScheduledRunConfig(scheduledRun, \"scheduled run config\");\n\tconst route = gatewayConfig.routes.find((candidate) => candidate.id === scheduledRun.routeId);\n\tif (!route) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} references unknown routeId ${scheduledRun.routeId}`);\n\t}\n\n\tconst webClient = new WebClient(gatewayConfig.botToken);\n\tconst blobStore = new LocalFileBlobStore(\n\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t);\n\tconst sink = new SlackSink(webClient, blobStore);\n\tconst workerClient = await createNatsBeeClient(gatewayConfig.nats);\n\n\ttry {\n\t\tconst auth = await webClient.auth.test();\n\t\tconst teamId = String(auth.team_id || \"unknown-team\");\n\t\tconst output = await resolveScheduledOutputTarget(webClient, scheduledRun);\n\t\tconst actor = await resolveScheduledActor(webClient, scheduledRun);\n\t\tlet statusRef: string | undefined;\n\t\tif (!output.threadId) {\n\t\t\tstatusRef = await sink.postMessage({ channelId: output.channelId }, \"_Working..._\");\n\t\t\toutput.threadId = statusRef;\n\t\t}\n\t\tconst slackConversationId = buildConversationId([\n\t\t\t\"slack\",\n\t\t\tteamId,\n\t\t\toutput.channelId || \"unknown-channel\",\n\t\t\toutput.threadId,\n\t\t]);\n\t\tconst conversationId = scheduledRun.conversationId || slackConversationId;\n\t\tconst sessionBase =\n\t\t\troute.session?.strategy === \"channel\" ? output.channelId || \"unknown-channel\" : output.threadId;\n\t\tconst routeSessionId = buildSessionKey(\n\t\t\tscheduledRun.sessionPrefix || route.session?.prefix || route.id,\n\t\t\tbuildConversationId([\"slack\", teamId, output.channelId || \"unknown-channel\", sessionBase || output.threadId]),\n\t\t);\n\t\tconst sessionId = scheduledRun.sessionId || routeSessionId;\n\n\t\tconst input: BeeResolvedTurn = {\n\t\t\tsessionId,\n\t\t\tthreadId: output.threadId,\n\t\t\tworker: route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId,\n\t\t\t},\n\t\t\tactor,\n\t\t\tmessage: {\n\t\t\t\ttext: scheduledRun.text,\n\t\t\t},\n\t\t\tattachments: [],\n\t\t\toutput,\n\t\t};\n\n\t\tlog.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);\n\t\tawait streamScheduledTurn(workerClient, sink, input, statusRef);\n\t\tlog.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog.logError(`Scheduled Slack run ${scheduledRun.id} failed`, message);\n\t\tthrow error;\n\t} finally {\n\t\tawait workerClient.close?.();\n\t}\n}\n\nasync function resolveScheduledOutputTarget(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<TransportOutputTarget> {\n\tif (scheduledRun.target.channelId) {\n\t\treturn {\n\t\t\tchannelId: scheduledRun.target.channelId,\n\t\t\tthreadId: scheduledRun.target.threadTs,\n\t\t};\n\t}\n\n\tif (!scheduledRun.target.slackUserId) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} needs target.slackUserId or target.channelId`);\n\t}\n\n\tconst result = await webClient.conversations.open({ users: scheduledRun.target.slackUserId });\n\tconst channel = result.channel as { id?: string } | undefined;\n\tif (!channel?.id) {\n\t\tthrow new Error(`Slack conversations.open did not return a channel for user ${scheduledRun.target.slackUserId}`);\n\t}\n\treturn {\n\t\tchannelId: channel.id,\n\t\tthreadId: scheduledRun.target.threadTs,\n\t};\n}\n\nasync function resolveScheduledActor(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<BeeResolvedTurn[\"actor\"]> {\n\tif (scheduledRun.actor) return scheduledRun.actor;\n\tif (!scheduledRun.target.slackUserId) {\n\t\treturn {\n\t\t\tuserId: `scheduler:${scheduledRun.id}`,\n\t\t\tdisplayName: \"Bee Scheduler\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst info = await webClient.users.info({ user: scheduledRun.target.slackUserId });\n\t\tconst user = info.user as\n\t\t\t| { id?: string; name?: string; real_name?: string; profile?: { display_name?: string } }\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tuserName: user?.name,\n\t\t\tdisplayName: user?.profile?.display_name || user?.real_name || user?.name,\n\t\t};\n\t} catch (error) {\n\t\tlog.logWarning(\n\t\t\t`Could not resolve Slack user ${scheduledRun.target.slackUserId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tdisplayName: scheduledRun.target.slackUserId,\n\t\t};\n\t}\n}\n\nasync function streamScheduledTurn(\n\tworkerClient: BeeWorkerClient,\n\tsink: SlackSink,\n\tinput: BeeResolvedTurn,\n\tstatusRef?: string,\n): Promise<void> {\n\tconst state: RenderState = {\n\t\tstatusRef,\n\t\tlatestText: \"_Working..._\",\n\t\trequestText: input.message.text,\n\t\titemTexts: new Map<string, string>(),\n\t};\n\tconst request = {\n\t\tsessionId: input.sessionId,\n\t\tthreadId: input.threadId,\n\t\tturnId: newTurnId(),\n\t\tconversation: input.conversation,\n\t\tactor: input.actor,\n\t\tmessage: input.message,\n\t\tattachments: input.attachments,\n\t};\n\n\ttry {\n\t\tawait workerClient.streamTurn(input.worker, request, async (event) => {\n\t\t\tawait handleScheduledEvent(sink, input.output, event, state);\n\t\t});\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait sink.postMessage(input.output, `_Scheduled gateway error: ${message}_`);\n\t\tthrow error;\n\t}\n}\n\nasync function handleScheduledEvent(\n\tsink: SlackSink,\n\toutput: TransportOutputTarget,\n\tevent: BeeRunEvent,\n\tstate: RenderState,\n): Promise<void> {\n\tif (event.name === \"run.started\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.completed\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.failed\") {\n\t\tconst errorText = `_Error: ${asRunFailedPayload(event).error}_`;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, errorText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, errorText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"approval.requested\") {\n\t\tawait sink.postMessage(output, `Approval requested: ${asApprovalRequestedPayload(event).summary}`);\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.appended\") {\n\t\tconst payload = asItemAppendedPayload(event);\n\t\tconst text = renderParts(payload.item.parts);\n\t\tstate.itemTexts.set(payload.item.id, text);\n\t\tif (payload.item.kind === \"artifact\") {\n\t\t\tconst artifact = firstArtifactRef(payload.item.parts);\n\t\t\tif (artifact && artifactHasPayload(artifact)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sink.publishArtifact(output, artifact);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tawait sink.postMessage(output, `${text}\\n_Artifact upload failed: ${message}_`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait sink.postMessage(output, text);\n\t\t\treturn;\n\t\t}\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, text);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.updated\") {\n\t\tconst payload = asItemUpdatedPayload(event);\n\t\tconst current = state.itemTexts.get(payload.itemId) || \"\";\n\t\tconst appended = renderParts(payload.appendParts || []);\n\t\tconst next = current ? `${current}${appended}` : appended;\n\t\tstate.itemTexts.set(payload.itemId, next);\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, next);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t}\n}\n\nfunction firstArtifactRef(parts: ItemPartLike[]): ArtifactRef | undefined {\n\tconst part = parts.find((entry) => entry.kind === \"artifactRef\");\n\tif (!part) return undefined;\n\treturn {\n\t\tartifactId: String(part.artifactId || \"artifact\"),\n\t\tblobKey: typeof part.blobKey === \"string\" ? part.blobKey : undefined,\n\t\tname: typeof part.name === \"string\" ? part.name : undefined,\n\t\ttitle: typeof part.title === \"string\" ? part.title : undefined,\n\t\tmimeType: typeof part.mimeType === \"string\" ? part.mimeType : undefined,\n\t\turi: typeof part.uri === \"string\" ? part.uri : undefined,\n\t\tsizeBytes: typeof part.sizeBytes === \"number\" ? part.sizeBytes : undefined,\n\t};\n}\n\nfunction artifactHasPayload(artifact: ArtifactRef): boolean {\n\treturn !!(artifact.uri || artifact.blobKey);\n}\n\nexport function renderScheduledFinalMessage(requestText: string | undefined, responseText: string): string {\n\tconst request = requestText?.trim();\n\tif (!request) return responseText;\n\n\tconst quotedRequest = request\n\t\t.split(/\\r?\\n/)\n\t\t.map((line) => `> ${escapeSlackText(line)}`)\n\t\t.join(\"\\n\");\n\n\treturn [\"*Request:*\", quotedRequest, \"*Antwort:*\", responseText].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction escapeSlackText(text: string): string {\n\treturn text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n}\n\nfunction renderParts(parts: ItemPartLike[]): string {\n\treturn parts\n\t\t.map((part) => {\n\t\t\tif (part.kind === \"text\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"status\") return String(part.status || \"\");\n\t\t\tif (part.kind === \"artifactRef\") {\n\t\t\t\treturn `Artifact: ${String(part.title || part.name || part.artifactId || \"artifact\")}`;\n\t\t\t}\n\t\t\tif (part.kind === \"approval\" || part.kind === \"choice\") {\n\t\t\t\treturn `${String(part.title || \"\")}\\n${String(part.summary || \"\")}`.trim();\n\t\t\t}\n\t\t\tif (part.kind === \"form\") return String(part.title || \"\");\n\t\t\tif (part.kind === \"log\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"patch\" || part.kind === \"diff\") {\n\t\t\t\tconst files = Array.isArray(part.files) ? (part.files as Array<{ path?: unknown }>) : [];\n\t\t\t\treturn files.map((entry) => `File: ${String(entry.path || \"\")}`).join(\"\\n\");\n\t\t\t}\n\t\t\treturn JSON.stringify(part);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction validateScheduledRunConfig(config: SlackScheduledRunConfig, source: string): void {\n\tif (!config.id) throw new Error(`Missing id in ${source}`);\n\tif (!config.routeId) throw new Error(`Missing routeId in ${source}`);\n\tif (!config.text) throw new Error(`Missing text in ${source}`);\n\tif (!config.target) throw new Error(`Missing target in ${source}`);\n\tif (!config.target.slackUserId && !config.target.channelId) {\n\t\tthrow new Error(`Missing target.slackUserId or target.channelId in ${source}`);\n\t}\n}\n\nfunction asRunFailedPayload(event: BeeRunEvent): RunFailedPayload {\n\treturn event.payload as RunFailedPayload;\n}\n\nfunction asApprovalRequestedPayload(event: BeeRunEvent): ApprovalRequestedPayload {\n\treturn event.payload as ApprovalRequestedPayload;\n}\n\nfunction asItemAppendedPayload(event: BeeRunEvent): ItemAppendedPayload {\n\treturn event.payload as ItemAppendedPayload;\n}\n\nfunction asItemUpdatedPayload(event: BeeRunEvent): ItemUpdatedPayload {\n\treturn event.payload as ItemUpdatedPayload;\n}\n"]}
package/dist/scheduled.js CHANGED
@@ -38,8 +38,10 @@ export async function runScheduledSlackTurn(gatewayConfig, scheduledRun) {
38
38
  const teamId = String(auth.team_id || "unknown-team");
39
39
  const output = await resolveScheduledOutputTarget(webClient, scheduledRun);
40
40
  const actor = await resolveScheduledActor(webClient, scheduledRun);
41
+ let statusRef;
41
42
  if (!output.threadId) {
42
- output.threadId = await sink.postMessage({ channelId: output.channelId }, `:alarm_clock: Scheduled Bee run \`${scheduledRun.id}\` started. Reply in this thread to continue the session.`);
43
+ statusRef = await sink.postMessage({ channelId: output.channelId }, "_Working..._");
44
+ output.threadId = statusRef;
43
45
  }
44
46
  const slackConversationId = buildConversationId([
45
47
  "slack",
@@ -67,7 +69,7 @@ export async function runScheduledSlackTurn(gatewayConfig, scheduledRun) {
67
69
  output,
68
70
  };
69
71
  log.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);
70
- await streamScheduledTurn(workerClient, sink, input);
72
+ await streamScheduledTurn(workerClient, sink, input, statusRef);
71
73
  log.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);
72
74
  }
73
75
  catch (error) {
@@ -125,9 +127,11 @@ async function resolveScheduledActor(webClient, scheduledRun) {
125
127
  };
126
128
  }
127
129
  }
128
- async function streamScheduledTurn(workerClient, sink, input) {
130
+ async function streamScheduledTurn(workerClient, sink, input, statusRef) {
129
131
  const state = {
132
+ statusRef,
130
133
  latestText: "_Working..._",
134
+ requestText: input.message.text,
131
135
  itemTexts: new Map(),
132
136
  };
133
137
  const request = {
@@ -152,7 +156,9 @@ async function streamScheduledTurn(workerClient, sink, input) {
152
156
  }
153
157
  async function handleScheduledEvent(sink, output, event, state) {
154
158
  if (event.name === "run.started") {
155
- state.statusRef = await sink.postMessage(output, state.latestText);
159
+ if (!state.statusRef) {
160
+ state.statusRef = await sink.postMessage(output, state.latestText);
161
+ }
156
162
  return;
157
163
  }
158
164
  if (event.name === "run.completed") {
@@ -195,12 +201,12 @@ async function handleScheduledEvent(sink, output, event, state) {
195
201
  await sink.postMessage(output, text);
196
202
  return;
197
203
  }
198
- state.latestText = text;
204
+ state.latestText = renderScheduledFinalMessage(state.requestText, text);
199
205
  if (state.statusRef) {
200
- await sink.updateMessage(output, state.statusRef, text);
206
+ await sink.updateMessage(output, state.statusRef, state.latestText);
201
207
  }
202
208
  else {
203
- state.statusRef = await sink.postMessage(output, text);
209
+ state.statusRef = await sink.postMessage(output, state.latestText);
204
210
  }
205
211
  return;
206
212
  }
@@ -210,12 +216,12 @@ async function handleScheduledEvent(sink, output, event, state) {
210
216
  const appended = renderParts(payload.appendParts || []);
211
217
  const next = current ? `${current}${appended}` : appended;
212
218
  state.itemTexts.set(payload.itemId, next);
213
- state.latestText = next;
219
+ state.latestText = renderScheduledFinalMessage(state.requestText, next);
214
220
  if (state.statusRef) {
215
- await sink.updateMessage(output, state.statusRef, next);
221
+ await sink.updateMessage(output, state.statusRef, state.latestText);
216
222
  }
217
223
  else {
218
- state.statusRef = await sink.postMessage(output, next);
224
+ state.statusRef = await sink.postMessage(output, state.latestText);
219
225
  }
220
226
  }
221
227
  }
@@ -236,6 +242,19 @@ function firstArtifactRef(parts) {
236
242
  function artifactHasPayload(artifact) {
237
243
  return !!(artifact.uri || artifact.blobKey);
238
244
  }
245
+ export function renderScheduledFinalMessage(requestText, responseText) {
246
+ const request = requestText?.trim();
247
+ if (!request)
248
+ return responseText;
249
+ const quotedRequest = request
250
+ .split(/\r?\n/)
251
+ .map((line) => `> ${escapeSlackText(line)}`)
252
+ .join("\n");
253
+ return ["*Request:*", quotedRequest, "*Antwort:*", responseText].filter(Boolean).join("\n\n");
254
+ }
255
+ function escapeSlackText(text) {
256
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
257
+ }
239
258
  function renderParts(parts) {
240
259
  return parts
241
260
  .map((part) => {
@@ -1 +1 @@
1
- {"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../src/scheduled.ts"],"names":[],"mappings":"AAAA,OAAO,EAKN,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,SAAS,GAET,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAkD5C,MAAM,UAAU,sBAAsB,CAAC,OAAgB,EAA2B;IACjF,MAAM,IAAI,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC;IACnE,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACd,qGAAqG,CACrG,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAA4B,CAAC;IACtF,0BAA0B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC;AAAA,CACd;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,UAAmB,EAAE,OAAgB,EAAiB;IAC1G,MAAM,aAAa,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,qBAAqB,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AAAA,CACzD;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,aAAiC,EACjC,YAAqC,EACrB;IAChB,0BAA0B,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,YAAY,CAAC,EAAE,+BAA+B,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC;IACxG,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,IAAI,kBAAkB,CACvC,OAAO,CAAC,GAAG,CAAC,yBAAyB;QACpC,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,OAAO,CAAC,GAAG,CAAC,qBAAqB;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,iBAAiB,CAAC,CACvC,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,MAAM,mBAAmB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAEnE,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,cAAc,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAC3E,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtB,MAAM,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CACvC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAC/B,qCAAqC,YAAY,CAAC,EAAE,2DAA2D,CAC/G,CAAC;QACH,CAAC;QACD,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;YAC/C,OAAO;YACP,MAAM;YACN,MAAM,CAAC,SAAS,IAAI,iBAAiB;YACrC,MAAM,CAAC,QAAQ;SACf,CAAC,CAAC;QACH,MAAM,cAAc,GAAG,YAAY,CAAC,cAAc,IAAI,mBAAmB,CAAC;QAC1E,MAAM,WAAW,GAChB,KAAK,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,IAAI,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QACjG,MAAM,cAAc,GAAG,eAAe,CACrC,YAAY,CAAC,aAAa,IAAI,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,EAAE,EAC/D,mBAAmB,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,SAAS,IAAI,iBAAiB,EAAE,WAAW,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAC7G,CAAC;QACF,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,IAAI,cAAc,CAAC;QAE3D,MAAM,KAAK,GAAoB;YAC9B,SAAS;YACT,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,YAAY,EAAE;gBACb,SAAS,EAAE,OAAO;gBAClB,cAAc;aACd;YACD,KAAK;YACL,OAAO,EAAE;gBACR,IAAI,EAAE,YAAY,CAAC,IAAI;aACvB;YACD,WAAW,EAAE,EAAE;YACf,MAAM;SACN,CAAC;QAEF,GAAG,CAAC,OAAO,CAAC,gCAAgC,YAAY,CAAC,EAAE,aAAa,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACpF,MAAM,mBAAmB,CAAC,YAAY,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACrD,GAAG,CAAC,OAAO,CAAC,iCAAiC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,GAAG,CAAC,QAAQ,CAAC,uBAAuB,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACvE,MAAM,KAAK,CAAC;IACb,CAAC;YAAS,CAAC;QACV,MAAM,YAAY,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,CAAC;AAAA,CACD;AAED,KAAK,UAAU,4BAA4B,CAC1C,SAAoB,EACpB,YAAqC,EACJ;IACjC,IAAI,YAAY,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACnC,OAAO;YACN,SAAS,EAAE,YAAY,CAAC,MAAM,CAAC,SAAS;YACxC,QAAQ,EAAE,YAAY,CAAC,MAAM,CAAC,QAAQ;SACtC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,iBAAiB,YAAY,CAAC,EAAE,+CAA+C,CAAC,CAAC;IAClG,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9F,MAAM,OAAO,GAAG,MAAM,CAAC,OAAsC,CAAC;IAC9D,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,8DAA8D,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAClH,CAAC;IACD,OAAO;QACN,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,QAAQ,EAAE,YAAY,CAAC,MAAM,CAAC,QAAQ;KACtC,CAAC;AAAA,CACF;AAED,KAAK,UAAU,qBAAqB,CACnC,SAAoB,EACpB,YAAqC,EACD;IACpC,IAAI,YAAY,CAAC,KAAK;QAAE,OAAO,YAAY,CAAC,KAAK,CAAC;IAClD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACtC,OAAO;YACN,MAAM,EAAE,aAAa,YAAY,CAAC,EAAE,EAAE;YACtC,WAAW,EAAE,eAAe;SAC5B,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QACnF,MAAM,IAAI,GAAG,IAAI,CAAC,IAEN,CAAC;QACb,OAAO;YACN,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW;YACvC,QAAQ,EAAE,IAAI,EAAE,IAAI;YACpB,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,IAAI,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE,IAAI;SACzE,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,GAAG,CAAC,UAAU,CACb,gCAAgC,YAAY,CAAC,MAAM,CAAC,WAAW,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC5H,CAAC;QACF,OAAO;YACN,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW;YACvC,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW;SAC5C,CAAC;IACH,CAAC;AAAA,CACD;AAED,KAAK,UAAU,mBAAmB,CACjC,YAA6B,EAC7B,IAAe,EACf,KAAsB,EACN;IAChB,MAAM,KAAK,GAAgB;QAC1B,UAAU,EAAE,cAAc;QAC1B,SAAS,EAAE,IAAI,GAAG,EAAkB;KACpC,CAAC;IACF,MAAM,OAAO,GAAG;QACf,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,SAAS,EAAE;QACnB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,WAAW,EAAE,KAAK,CAAC,WAAW;KAC9B,CAAC;IAEF,IAAI,CAAC;QACJ,MAAM,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;YACrE,MAAM,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAAA,CAC7D,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,6BAA6B,OAAO,GAAG,CAAC,CAAC;QAC9E,MAAM,KAAK,CAAC;IACb,CAAC;AAAA,CACD;AAED,KAAK,UAAU,oBAAoB,CAClC,IAAe,EACf,MAA6B,EAC7B,KAAkB,EAClB,KAAkB,EACF;IAChB,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACnE,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACpC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACtB,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACpE,CAAC;QACD,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,WAAW,kBAAkB,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC;QAChE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;QACzC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,uBAAuB,0BAA0B,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACnG,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtD,IAAI,QAAQ,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9C,IAAI,CAAC;oBACJ,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;oBAC7C,OAAO;gBACR,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACvE,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,GAAG,IAAI,8BAA8B,OAAO,GAAG,CAAC,CAAC;oBAChF,OAAO;gBACR,CAAC;YACF,CAAC;YACD,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACrC,OAAO;QACR,CAAC;QACD,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;QACxB,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACxD,CAAC;QACD,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC1D,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1D,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1C,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;QACxB,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACxD,CAAC;IACF,CAAC;AAAA,CACD;AAED,SAAS,gBAAgB,CAAC,KAAqB,EAA2B;IACzE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;IACjE,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,OAAO;QACN,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC;QACjD,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACpE,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QAC3D,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC9D,QAAQ,EAAE,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;QACvE,GAAG,EAAE,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QACxD,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;KAC1E,CAAC;AAAA,CACF;AAED,SAAS,kBAAkB,CAAC,QAAqB,EAAW;IAC3D,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;AAAA,CAC5C;AAED,SAAS,WAAW,CAAC,KAAqB,EAAU;IACnD,OAAO,KAAK;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACzD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QAC7D,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YACjC,OAAO,aAAa,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,EAAE,CAAC;QACxF,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACxD,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5E,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC1D,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACxD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,KAAmC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAAA,CAC5B,CAAC;SACD,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACb;AAED,SAAS,0BAA0B,CAAC,MAA+B,EAAE,MAAc,EAAQ;IAC1F,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,MAAM,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,EAAE,CAAC,CAAC;IACnE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,qDAAqD,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;AAAA,CACD;AAED,SAAS,kBAAkB,CAAC,KAAkB,EAAoB;IACjE,OAAO,KAAK,CAAC,OAA2B,CAAC;AAAA,CACzC;AAED,SAAS,0BAA0B,CAAC,KAAkB,EAA4B;IACjF,OAAO,KAAK,CAAC,OAAmC,CAAC;AAAA,CACjD;AAED,SAAS,qBAAqB,CAAC,KAAkB,EAAuB;IACvE,OAAO,KAAK,CAAC,OAA8B,CAAC;AAAA,CAC5C;AAED,SAAS,oBAAoB,CAAC,KAAkB,EAAsB;IACrE,OAAO,KAAK,CAAC,OAA6B,CAAC;AAAA,CAC3C","sourcesContent":["import {\n\ttype ArtifactRef,\n\ttype BeeResolvedTurn,\n\ttype BeeRunEvent,\n\ttype BeeWorkerClient,\n\tbuildConversationId,\n\tbuildSessionKey,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\tnewTurnId,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { WebClient } from \"@slack/web-api\";\nimport { readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport * as log from \"./log.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport type { SlackGatewayConfig } from \"./types.js\";\n\nexport interface SlackScheduledRunConfig {\n\tid: string;\n\trouteId: string;\n\ttext: string;\n\ttarget: {\n\t\tslackUserId?: string;\n\t\tchannelId?: string;\n\t\tthreadTs?: string;\n\t};\n\tactor?: {\n\t\tuserId: string;\n\t\tuserName?: string;\n\t\tdisplayName?: string;\n\t};\n\tsessionId?: string;\n\tsessionPrefix?: string;\n\tconversationId?: string;\n}\n\ninterface RenderState {\n\tstatusRef?: string;\n\tlatestText: string;\n\titemTexts: Map<string, string>;\n}\n\ntype ItemPartLike = { kind: string; [key: string]: unknown };\n\ntype ItemAppendedPayload = {\n\teventType: \"item.appended\";\n\titem: { id: string; kind: string; role: string; parts: ItemPartLike[] };\n};\n\ntype ItemUpdatedPayload = {\n\teventType: \"item.updated\";\n\titemId: string;\n\tappendParts?: ItemPartLike[];\n};\n\ntype RunFailedPayload = {\n\teventType: \"run.failed\";\n\terror: string;\n};\n\ntype ApprovalRequestedPayload = {\n\tsummary: string;\n};\n\nexport function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig {\n\tconst path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;\n\tif (!path) {\n\t\tthrow new Error(\n\t\t\t\"Missing scheduled run config path; pass it as second argument or set BEE_SLACK_SCHEDULED_RUN_CONFIG\",\n\t\t);\n\t}\n\n\tconst fullPath = resolve(path);\n\tconst config = JSON.parse(readFileSync(fullPath, \"utf-8\")) as SlackScheduledRunConfig;\n\tvalidateScheduledRunConfig(config, fullPath);\n\treturn config;\n}\n\nexport async function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void> {\n\tconst gatewayConfig = loadConfig(configPath);\n\tconst scheduledRun = loadScheduledRunConfig(jobPath);\n\tawait runScheduledSlackTurn(gatewayConfig, scheduledRun);\n}\n\nexport async function runScheduledSlackTurn(\n\tgatewayConfig: SlackGatewayConfig,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<void> {\n\tvalidateScheduledRunConfig(scheduledRun, \"scheduled run config\");\n\tconst route = gatewayConfig.routes.find((candidate) => candidate.id === scheduledRun.routeId);\n\tif (!route) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} references unknown routeId ${scheduledRun.routeId}`);\n\t}\n\n\tconst webClient = new WebClient(gatewayConfig.botToken);\n\tconst blobStore = new LocalFileBlobStore(\n\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t);\n\tconst sink = new SlackSink(webClient, blobStore);\n\tconst workerClient = await createNatsBeeClient(gatewayConfig.nats);\n\n\ttry {\n\t\tconst auth = await webClient.auth.test();\n\t\tconst teamId = String(auth.team_id || \"unknown-team\");\n\t\tconst output = await resolveScheduledOutputTarget(webClient, scheduledRun);\n\t\tconst actor = await resolveScheduledActor(webClient, scheduledRun);\n\t\tif (!output.threadId) {\n\t\t\toutput.threadId = await sink.postMessage(\n\t\t\t\t{ channelId: output.channelId },\n\t\t\t\t`:alarm_clock: Scheduled Bee run \\`${scheduledRun.id}\\` started. Reply in this thread to continue the session.`,\n\t\t\t);\n\t\t}\n\t\tconst slackConversationId = buildConversationId([\n\t\t\t\"slack\",\n\t\t\tteamId,\n\t\t\toutput.channelId || \"unknown-channel\",\n\t\t\toutput.threadId,\n\t\t]);\n\t\tconst conversationId = scheduledRun.conversationId || slackConversationId;\n\t\tconst sessionBase =\n\t\t\troute.session?.strategy === \"channel\" ? output.channelId || \"unknown-channel\" : output.threadId;\n\t\tconst routeSessionId = buildSessionKey(\n\t\t\tscheduledRun.sessionPrefix || route.session?.prefix || route.id,\n\t\t\tbuildConversationId([\"slack\", teamId, output.channelId || \"unknown-channel\", sessionBase || output.threadId]),\n\t\t);\n\t\tconst sessionId = scheduledRun.sessionId || routeSessionId;\n\n\t\tconst input: BeeResolvedTurn = {\n\t\t\tsessionId,\n\t\t\tthreadId: output.threadId,\n\t\t\tworker: route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId,\n\t\t\t},\n\t\t\tactor,\n\t\t\tmessage: {\n\t\t\t\ttext: scheduledRun.text,\n\t\t\t},\n\t\t\tattachments: [],\n\t\t\toutput,\n\t\t};\n\n\t\tlog.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);\n\t\tawait streamScheduledTurn(workerClient, sink, input);\n\t\tlog.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog.logError(`Scheduled Slack run ${scheduledRun.id} failed`, message);\n\t\tthrow error;\n\t} finally {\n\t\tawait workerClient.close?.();\n\t}\n}\n\nasync function resolveScheduledOutputTarget(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<TransportOutputTarget> {\n\tif (scheduledRun.target.channelId) {\n\t\treturn {\n\t\t\tchannelId: scheduledRun.target.channelId,\n\t\t\tthreadId: scheduledRun.target.threadTs,\n\t\t};\n\t}\n\n\tif (!scheduledRun.target.slackUserId) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} needs target.slackUserId or target.channelId`);\n\t}\n\n\tconst result = await webClient.conversations.open({ users: scheduledRun.target.slackUserId });\n\tconst channel = result.channel as { id?: string } | undefined;\n\tif (!channel?.id) {\n\t\tthrow new Error(`Slack conversations.open did not return a channel for user ${scheduledRun.target.slackUserId}`);\n\t}\n\treturn {\n\t\tchannelId: channel.id,\n\t\tthreadId: scheduledRun.target.threadTs,\n\t};\n}\n\nasync function resolveScheduledActor(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<BeeResolvedTurn[\"actor\"]> {\n\tif (scheduledRun.actor) return scheduledRun.actor;\n\tif (!scheduledRun.target.slackUserId) {\n\t\treturn {\n\t\t\tuserId: `scheduler:${scheduledRun.id}`,\n\t\t\tdisplayName: \"Bee Scheduler\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst info = await webClient.users.info({ user: scheduledRun.target.slackUserId });\n\t\tconst user = info.user as\n\t\t\t| { id?: string; name?: string; real_name?: string; profile?: { display_name?: string } }\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tuserName: user?.name,\n\t\t\tdisplayName: user?.profile?.display_name || user?.real_name || user?.name,\n\t\t};\n\t} catch (error) {\n\t\tlog.logWarning(\n\t\t\t`Could not resolve Slack user ${scheduledRun.target.slackUserId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tdisplayName: scheduledRun.target.slackUserId,\n\t\t};\n\t}\n}\n\nasync function streamScheduledTurn(\n\tworkerClient: BeeWorkerClient,\n\tsink: SlackSink,\n\tinput: BeeResolvedTurn,\n): Promise<void> {\n\tconst state: RenderState = {\n\t\tlatestText: \"_Working..._\",\n\t\titemTexts: new Map<string, string>(),\n\t};\n\tconst request = {\n\t\tsessionId: input.sessionId,\n\t\tthreadId: input.threadId,\n\t\tturnId: newTurnId(),\n\t\tconversation: input.conversation,\n\t\tactor: input.actor,\n\t\tmessage: input.message,\n\t\tattachments: input.attachments,\n\t};\n\n\ttry {\n\t\tawait workerClient.streamTurn(input.worker, request, async (event) => {\n\t\t\tawait handleScheduledEvent(sink, input.output, event, state);\n\t\t});\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait sink.postMessage(input.output, `_Scheduled gateway error: ${message}_`);\n\t\tthrow error;\n\t}\n}\n\nasync function handleScheduledEvent(\n\tsink: SlackSink,\n\toutput: TransportOutputTarget,\n\tevent: BeeRunEvent,\n\tstate: RenderState,\n): Promise<void> {\n\tif (event.name === \"run.started\") {\n\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.completed\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.failed\") {\n\t\tconst errorText = `_Error: ${asRunFailedPayload(event).error}_`;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, errorText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, errorText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"approval.requested\") {\n\t\tawait sink.postMessage(output, `Approval requested: ${asApprovalRequestedPayload(event).summary}`);\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.appended\") {\n\t\tconst payload = asItemAppendedPayload(event);\n\t\tconst text = renderParts(payload.item.parts);\n\t\tstate.itemTexts.set(payload.item.id, text);\n\t\tif (payload.item.kind === \"artifact\") {\n\t\t\tconst artifact = firstArtifactRef(payload.item.parts);\n\t\t\tif (artifact && artifactHasPayload(artifact)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sink.publishArtifact(output, artifact);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tawait sink.postMessage(output, `${text}\\n_Artifact upload failed: ${message}_`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait sink.postMessage(output, text);\n\t\t\treturn;\n\t\t}\n\t\tstate.latestText = text;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, text);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, text);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.updated\") {\n\t\tconst payload = asItemUpdatedPayload(event);\n\t\tconst current = state.itemTexts.get(payload.itemId) || \"\";\n\t\tconst appended = renderParts(payload.appendParts || []);\n\t\tconst next = current ? `${current}${appended}` : appended;\n\t\tstate.itemTexts.set(payload.itemId, next);\n\t\tstate.latestText = next;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, next);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, next);\n\t\t}\n\t}\n}\n\nfunction firstArtifactRef(parts: ItemPartLike[]): ArtifactRef | undefined {\n\tconst part = parts.find((entry) => entry.kind === \"artifactRef\");\n\tif (!part) return undefined;\n\treturn {\n\t\tartifactId: String(part.artifactId || \"artifact\"),\n\t\tblobKey: typeof part.blobKey === \"string\" ? part.blobKey : undefined,\n\t\tname: typeof part.name === \"string\" ? part.name : undefined,\n\t\ttitle: typeof part.title === \"string\" ? part.title : undefined,\n\t\tmimeType: typeof part.mimeType === \"string\" ? part.mimeType : undefined,\n\t\turi: typeof part.uri === \"string\" ? part.uri : undefined,\n\t\tsizeBytes: typeof part.sizeBytes === \"number\" ? part.sizeBytes : undefined,\n\t};\n}\n\nfunction artifactHasPayload(artifact: ArtifactRef): boolean {\n\treturn !!(artifact.uri || artifact.blobKey);\n}\n\nfunction renderParts(parts: ItemPartLike[]): string {\n\treturn parts\n\t\t.map((part) => {\n\t\t\tif (part.kind === \"text\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"status\") return String(part.status || \"\");\n\t\t\tif (part.kind === \"artifactRef\") {\n\t\t\t\treturn `Artifact: ${String(part.title || part.name || part.artifactId || \"artifact\")}`;\n\t\t\t}\n\t\t\tif (part.kind === \"approval\" || part.kind === \"choice\") {\n\t\t\t\treturn `${String(part.title || \"\")}\\n${String(part.summary || \"\")}`.trim();\n\t\t\t}\n\t\t\tif (part.kind === \"form\") return String(part.title || \"\");\n\t\t\tif (part.kind === \"log\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"patch\" || part.kind === \"diff\") {\n\t\t\t\tconst files = Array.isArray(part.files) ? (part.files as Array<{ path?: unknown }>) : [];\n\t\t\t\treturn files.map((entry) => `File: ${String(entry.path || \"\")}`).join(\"\\n\");\n\t\t\t}\n\t\t\treturn JSON.stringify(part);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction validateScheduledRunConfig(config: SlackScheduledRunConfig, source: string): void {\n\tif (!config.id) throw new Error(`Missing id in ${source}`);\n\tif (!config.routeId) throw new Error(`Missing routeId in ${source}`);\n\tif (!config.text) throw new Error(`Missing text in ${source}`);\n\tif (!config.target) throw new Error(`Missing target in ${source}`);\n\tif (!config.target.slackUserId && !config.target.channelId) {\n\t\tthrow new Error(`Missing target.slackUserId or target.channelId in ${source}`);\n\t}\n}\n\nfunction asRunFailedPayload(event: BeeRunEvent): RunFailedPayload {\n\treturn event.payload as RunFailedPayload;\n}\n\nfunction asApprovalRequestedPayload(event: BeeRunEvent): ApprovalRequestedPayload {\n\treturn event.payload as ApprovalRequestedPayload;\n}\n\nfunction asItemAppendedPayload(event: BeeRunEvent): ItemAppendedPayload {\n\treturn event.payload as ItemAppendedPayload;\n}\n\nfunction asItemUpdatedPayload(event: BeeRunEvent): ItemUpdatedPayload {\n\treturn event.payload as ItemUpdatedPayload;\n}\n"]}
1
+ {"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../src/scheduled.ts"],"names":[],"mappings":"AAAA,OAAO,EAKN,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,SAAS,GAET,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAmD5C,MAAM,UAAU,sBAAsB,CAAC,OAAgB,EAA2B;IACjF,MAAM,IAAI,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC;IACnE,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACd,qGAAqG,CACrG,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAA4B,CAAC;IACtF,0BAA0B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC;AAAA,CACd;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,UAAmB,EAAE,OAAgB,EAAiB;IAC1G,MAAM,aAAa,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,qBAAqB,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AAAA,CACzD;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,aAAiC,EACjC,YAAqC,EACrB;IAChB,0BAA0B,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,YAAY,CAAC,EAAE,+BAA+B,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC;IACxG,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,IAAI,kBAAkB,CACvC,OAAO,CAAC,GAAG,CAAC,yBAAyB;QACpC,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,OAAO,CAAC,GAAG,CAAC,qBAAqB;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,iBAAiB,CAAC,CACvC,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,MAAM,mBAAmB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAEnE,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,cAAc,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAC3E,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QACnE,IAAI,SAA6B,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtB,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,cAAc,CAAC,CAAC;YACpF,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC7B,CAAC;QACD,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;YAC/C,OAAO;YACP,MAAM;YACN,MAAM,CAAC,SAAS,IAAI,iBAAiB;YACrC,MAAM,CAAC,QAAQ;SACf,CAAC,CAAC;QACH,MAAM,cAAc,GAAG,YAAY,CAAC,cAAc,IAAI,mBAAmB,CAAC;QAC1E,MAAM,WAAW,GAChB,KAAK,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,IAAI,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QACjG,MAAM,cAAc,GAAG,eAAe,CACrC,YAAY,CAAC,aAAa,IAAI,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,EAAE,EAC/D,mBAAmB,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,SAAS,IAAI,iBAAiB,EAAE,WAAW,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAC7G,CAAC;QACF,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,IAAI,cAAc,CAAC;QAE3D,MAAM,KAAK,GAAoB;YAC9B,SAAS;YACT,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,YAAY,EAAE;gBACb,SAAS,EAAE,OAAO;gBAClB,cAAc;aACd;YACD,KAAK;YACL,OAAO,EAAE;gBACR,IAAI,EAAE,YAAY,CAAC,IAAI;aACvB;YACD,WAAW,EAAE,EAAE;YACf,MAAM;SACN,CAAC;QAEF,GAAG,CAAC,OAAO,CAAC,gCAAgC,YAAY,CAAC,EAAE,aAAa,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACpF,MAAM,mBAAmB,CAAC,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,iCAAiC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,GAAG,CAAC,QAAQ,CAAC,uBAAuB,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACvE,MAAM,KAAK,CAAC;IACb,CAAC;YAAS,CAAC;QACV,MAAM,YAAY,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,CAAC;AAAA,CACD;AAED,KAAK,UAAU,4BAA4B,CAC1C,SAAoB,EACpB,YAAqC,EACJ;IACjC,IAAI,YAAY,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACnC,OAAO;YACN,SAAS,EAAE,YAAY,CAAC,MAAM,CAAC,SAAS;YACxC,QAAQ,EAAE,YAAY,CAAC,MAAM,CAAC,QAAQ;SACtC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,iBAAiB,YAAY,CAAC,EAAE,+CAA+C,CAAC,CAAC;IAClG,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9F,MAAM,OAAO,GAAG,MAAM,CAAC,OAAsC,CAAC;IAC9D,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,8DAA8D,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAClH,CAAC;IACD,OAAO;QACN,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,QAAQ,EAAE,YAAY,CAAC,MAAM,CAAC,QAAQ;KACtC,CAAC;AAAA,CACF;AAED,KAAK,UAAU,qBAAqB,CACnC,SAAoB,EACpB,YAAqC,EACD;IACpC,IAAI,YAAY,CAAC,KAAK;QAAE,OAAO,YAAY,CAAC,KAAK,CAAC;IAClD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACtC,OAAO;YACN,MAAM,EAAE,aAAa,YAAY,CAAC,EAAE,EAAE;YACtC,WAAW,EAAE,eAAe;SAC5B,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QACnF,MAAM,IAAI,GAAG,IAAI,CAAC,IAEN,CAAC;QACb,OAAO;YACN,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW;YACvC,QAAQ,EAAE,IAAI,EAAE,IAAI;YACpB,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,IAAI,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE,IAAI;SACzE,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,GAAG,CAAC,UAAU,CACb,gCAAgC,YAAY,CAAC,MAAM,CAAC,WAAW,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC5H,CAAC;QACF,OAAO;YACN,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW;YACvC,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW;SAC5C,CAAC;IACH,CAAC;AAAA,CACD;AAED,KAAK,UAAU,mBAAmB,CACjC,YAA6B,EAC7B,IAAe,EACf,KAAsB,EACtB,SAAkB,EACF;IAChB,MAAM,KAAK,GAAgB;QAC1B,SAAS;QACT,UAAU,EAAE,cAAc;QAC1B,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI;QAC/B,SAAS,EAAE,IAAI,GAAG,EAAkB;KACpC,CAAC;IACF,MAAM,OAAO,GAAG;QACf,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,SAAS,EAAE;QACnB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,WAAW,EAAE,KAAK,CAAC,WAAW;KAC9B,CAAC;IAEF,IAAI,CAAC;QACJ,MAAM,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;YACrE,MAAM,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAAA,CAC7D,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,6BAA6B,OAAO,GAAG,CAAC,CAAC;QAC9E,MAAM,KAAK,CAAC;IACb,CAAC;AAAA,CACD;AAED,KAAK,UAAU,oBAAoB,CAClC,IAAe,EACf,MAA6B,EAC7B,KAAkB,EAClB,KAAkB,EACF;IAChB,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACtB,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACpE,CAAC;QACD,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACpC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACtB,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACpE,CAAC;QACD,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,WAAW,kBAAkB,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC;QAChE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;QACzC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,uBAAuB,0BAA0B,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACnG,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtD,IAAI,QAAQ,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9C,IAAI,CAAC;oBACJ,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;oBAC7C,OAAO;gBACR,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACvE,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,GAAG,IAAI,8BAA8B,OAAO,GAAG,CAAC,CAAC;oBAChF,OAAO;gBACR,CAAC;YACF,CAAC;YACD,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACrC,OAAO;QACR,CAAC;QACD,KAAK,CAAC,UAAU,GAAG,2BAA2B,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACxE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACrE,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACpE,CAAC;QACD,OAAO;IACR,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC1D,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1D,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1C,KAAK,CAAC,UAAU,GAAG,2BAA2B,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACxE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACrE,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACpE,CAAC;IACF,CAAC;AAAA,CACD;AAED,SAAS,gBAAgB,CAAC,KAAqB,EAA2B;IACzE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;IACjE,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,OAAO;QACN,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC;QACjD,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACpE,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QAC3D,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC9D,QAAQ,EAAE,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;QACvE,GAAG,EAAE,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QACxD,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;KAC1E,CAAC;AAAA,CACF;AAED,SAAS,kBAAkB,CAAC,QAAqB,EAAW;IAC3D,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;AAAA,CAC5C;AAED,MAAM,UAAU,2BAA2B,CAAC,WAA+B,EAAE,YAAoB,EAAU;IAC1G,MAAM,OAAO,GAAG,WAAW,EAAE,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO;QAAE,OAAO,YAAY,CAAC;IAElC,MAAM,aAAa,GAAG,OAAO;SAC3B,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;SAC3C,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO,CAAC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAAA,CAC9F;AAED,SAAS,eAAe,CAAC,IAAY,EAAU;IAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAAA,CAC/E;AAED,SAAS,WAAW,CAAC,KAAqB,EAAU;IACnD,OAAO,KAAK;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACzD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QAC7D,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YACjC,OAAO,aAAa,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,EAAE,CAAC;QACxF,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACxD,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5E,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC1D,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACxD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,KAAmC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAAA,CAC5B,CAAC;SACD,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACb;AAED,SAAS,0BAA0B,CAAC,MAA+B,EAAE,MAAc,EAAQ;IAC1F,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,MAAM,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,EAAE,CAAC,CAAC;IACnE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,qDAAqD,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;AAAA,CACD;AAED,SAAS,kBAAkB,CAAC,KAAkB,EAAoB;IACjE,OAAO,KAAK,CAAC,OAA2B,CAAC;AAAA,CACzC;AAED,SAAS,0BAA0B,CAAC,KAAkB,EAA4B;IACjF,OAAO,KAAK,CAAC,OAAmC,CAAC;AAAA,CACjD;AAED,SAAS,qBAAqB,CAAC,KAAkB,EAAuB;IACvE,OAAO,KAAK,CAAC,OAA8B,CAAC;AAAA,CAC5C;AAED,SAAS,oBAAoB,CAAC,KAAkB,EAAsB;IACrE,OAAO,KAAK,CAAC,OAA6B,CAAC;AAAA,CAC3C","sourcesContent":["import {\n\ttype ArtifactRef,\n\ttype BeeResolvedTurn,\n\ttype BeeRunEvent,\n\ttype BeeWorkerClient,\n\tbuildConversationId,\n\tbuildSessionKey,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\tnewTurnId,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { WebClient } from \"@slack/web-api\";\nimport { readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport * as log from \"./log.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport type { SlackGatewayConfig } from \"./types.js\";\n\nexport interface SlackScheduledRunConfig {\n\tid: string;\n\trouteId: string;\n\ttext: string;\n\ttarget: {\n\t\tslackUserId?: string;\n\t\tchannelId?: string;\n\t\tthreadTs?: string;\n\t};\n\tactor?: {\n\t\tuserId: string;\n\t\tuserName?: string;\n\t\tdisplayName?: string;\n\t};\n\tsessionId?: string;\n\tsessionPrefix?: string;\n\tconversationId?: string;\n}\n\ninterface RenderState {\n\tstatusRef?: string;\n\tlatestText: string;\n\trequestText?: string;\n\titemTexts: Map<string, string>;\n}\n\ntype ItemPartLike = { kind: string; [key: string]: unknown };\n\ntype ItemAppendedPayload = {\n\teventType: \"item.appended\";\n\titem: { id: string; kind: string; role: string; parts: ItemPartLike[] };\n};\n\ntype ItemUpdatedPayload = {\n\teventType: \"item.updated\";\n\titemId: string;\n\tappendParts?: ItemPartLike[];\n};\n\ntype RunFailedPayload = {\n\teventType: \"run.failed\";\n\terror: string;\n};\n\ntype ApprovalRequestedPayload = {\n\tsummary: string;\n};\n\nexport function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig {\n\tconst path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;\n\tif (!path) {\n\t\tthrow new Error(\n\t\t\t\"Missing scheduled run config path; pass it as second argument or set BEE_SLACK_SCHEDULED_RUN_CONFIG\",\n\t\t);\n\t}\n\n\tconst fullPath = resolve(path);\n\tconst config = JSON.parse(readFileSync(fullPath, \"utf-8\")) as SlackScheduledRunConfig;\n\tvalidateScheduledRunConfig(config, fullPath);\n\treturn config;\n}\n\nexport async function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void> {\n\tconst gatewayConfig = loadConfig(configPath);\n\tconst scheduledRun = loadScheduledRunConfig(jobPath);\n\tawait runScheduledSlackTurn(gatewayConfig, scheduledRun);\n}\n\nexport async function runScheduledSlackTurn(\n\tgatewayConfig: SlackGatewayConfig,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<void> {\n\tvalidateScheduledRunConfig(scheduledRun, \"scheduled run config\");\n\tconst route = gatewayConfig.routes.find((candidate) => candidate.id === scheduledRun.routeId);\n\tif (!route) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} references unknown routeId ${scheduledRun.routeId}`);\n\t}\n\n\tconst webClient = new WebClient(gatewayConfig.botToken);\n\tconst blobStore = new LocalFileBlobStore(\n\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t);\n\tconst sink = new SlackSink(webClient, blobStore);\n\tconst workerClient = await createNatsBeeClient(gatewayConfig.nats);\n\n\ttry {\n\t\tconst auth = await webClient.auth.test();\n\t\tconst teamId = String(auth.team_id || \"unknown-team\");\n\t\tconst output = await resolveScheduledOutputTarget(webClient, scheduledRun);\n\t\tconst actor = await resolveScheduledActor(webClient, scheduledRun);\n\t\tlet statusRef: string | undefined;\n\t\tif (!output.threadId) {\n\t\t\tstatusRef = await sink.postMessage({ channelId: output.channelId }, \"_Working..._\");\n\t\t\toutput.threadId = statusRef;\n\t\t}\n\t\tconst slackConversationId = buildConversationId([\n\t\t\t\"slack\",\n\t\t\tteamId,\n\t\t\toutput.channelId || \"unknown-channel\",\n\t\t\toutput.threadId,\n\t\t]);\n\t\tconst conversationId = scheduledRun.conversationId || slackConversationId;\n\t\tconst sessionBase =\n\t\t\troute.session?.strategy === \"channel\" ? output.channelId || \"unknown-channel\" : output.threadId;\n\t\tconst routeSessionId = buildSessionKey(\n\t\t\tscheduledRun.sessionPrefix || route.session?.prefix || route.id,\n\t\t\tbuildConversationId([\"slack\", teamId, output.channelId || \"unknown-channel\", sessionBase || output.threadId]),\n\t\t);\n\t\tconst sessionId = scheduledRun.sessionId || routeSessionId;\n\n\t\tconst input: BeeResolvedTurn = {\n\t\t\tsessionId,\n\t\t\tthreadId: output.threadId,\n\t\t\tworker: route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId,\n\t\t\t},\n\t\t\tactor,\n\t\t\tmessage: {\n\t\t\t\ttext: scheduledRun.text,\n\t\t\t},\n\t\t\tattachments: [],\n\t\t\toutput,\n\t\t};\n\n\t\tlog.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);\n\t\tawait streamScheduledTurn(workerClient, sink, input, statusRef);\n\t\tlog.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog.logError(`Scheduled Slack run ${scheduledRun.id} failed`, message);\n\t\tthrow error;\n\t} finally {\n\t\tawait workerClient.close?.();\n\t}\n}\n\nasync function resolveScheduledOutputTarget(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<TransportOutputTarget> {\n\tif (scheduledRun.target.channelId) {\n\t\treturn {\n\t\t\tchannelId: scheduledRun.target.channelId,\n\t\t\tthreadId: scheduledRun.target.threadTs,\n\t\t};\n\t}\n\n\tif (!scheduledRun.target.slackUserId) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} needs target.slackUserId or target.channelId`);\n\t}\n\n\tconst result = await webClient.conversations.open({ users: scheduledRun.target.slackUserId });\n\tconst channel = result.channel as { id?: string } | undefined;\n\tif (!channel?.id) {\n\t\tthrow new Error(`Slack conversations.open did not return a channel for user ${scheduledRun.target.slackUserId}`);\n\t}\n\treturn {\n\t\tchannelId: channel.id,\n\t\tthreadId: scheduledRun.target.threadTs,\n\t};\n}\n\nasync function resolveScheduledActor(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<BeeResolvedTurn[\"actor\"]> {\n\tif (scheduledRun.actor) return scheduledRun.actor;\n\tif (!scheduledRun.target.slackUserId) {\n\t\treturn {\n\t\t\tuserId: `scheduler:${scheduledRun.id}`,\n\t\t\tdisplayName: \"Bee Scheduler\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst info = await webClient.users.info({ user: scheduledRun.target.slackUserId });\n\t\tconst user = info.user as\n\t\t\t| { id?: string; name?: string; real_name?: string; profile?: { display_name?: string } }\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tuserName: user?.name,\n\t\t\tdisplayName: user?.profile?.display_name || user?.real_name || user?.name,\n\t\t};\n\t} catch (error) {\n\t\tlog.logWarning(\n\t\t\t`Could not resolve Slack user ${scheduledRun.target.slackUserId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tdisplayName: scheduledRun.target.slackUserId,\n\t\t};\n\t}\n}\n\nasync function streamScheduledTurn(\n\tworkerClient: BeeWorkerClient,\n\tsink: SlackSink,\n\tinput: BeeResolvedTurn,\n\tstatusRef?: string,\n): Promise<void> {\n\tconst state: RenderState = {\n\t\tstatusRef,\n\t\tlatestText: \"_Working..._\",\n\t\trequestText: input.message.text,\n\t\titemTexts: new Map<string, string>(),\n\t};\n\tconst request = {\n\t\tsessionId: input.sessionId,\n\t\tthreadId: input.threadId,\n\t\tturnId: newTurnId(),\n\t\tconversation: input.conversation,\n\t\tactor: input.actor,\n\t\tmessage: input.message,\n\t\tattachments: input.attachments,\n\t};\n\n\ttry {\n\t\tawait workerClient.streamTurn(input.worker, request, async (event) => {\n\t\t\tawait handleScheduledEvent(sink, input.output, event, state);\n\t\t});\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait sink.postMessage(input.output, `_Scheduled gateway error: ${message}_`);\n\t\tthrow error;\n\t}\n}\n\nasync function handleScheduledEvent(\n\tsink: SlackSink,\n\toutput: TransportOutputTarget,\n\tevent: BeeRunEvent,\n\tstate: RenderState,\n): Promise<void> {\n\tif (event.name === \"run.started\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.completed\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.failed\") {\n\t\tconst errorText = `_Error: ${asRunFailedPayload(event).error}_`;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, errorText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, errorText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"approval.requested\") {\n\t\tawait sink.postMessage(output, `Approval requested: ${asApprovalRequestedPayload(event).summary}`);\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.appended\") {\n\t\tconst payload = asItemAppendedPayload(event);\n\t\tconst text = renderParts(payload.item.parts);\n\t\tstate.itemTexts.set(payload.item.id, text);\n\t\tif (payload.item.kind === \"artifact\") {\n\t\t\tconst artifact = firstArtifactRef(payload.item.parts);\n\t\t\tif (artifact && artifactHasPayload(artifact)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sink.publishArtifact(output, artifact);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tawait sink.postMessage(output, `${text}\\n_Artifact upload failed: ${message}_`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait sink.postMessage(output, text);\n\t\t\treturn;\n\t\t}\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, text);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.updated\") {\n\t\tconst payload = asItemUpdatedPayload(event);\n\t\tconst current = state.itemTexts.get(payload.itemId) || \"\";\n\t\tconst appended = renderParts(payload.appendParts || []);\n\t\tconst next = current ? `${current}${appended}` : appended;\n\t\tstate.itemTexts.set(payload.itemId, next);\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, next);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t}\n}\n\nfunction firstArtifactRef(parts: ItemPartLike[]): ArtifactRef | undefined {\n\tconst part = parts.find((entry) => entry.kind === \"artifactRef\");\n\tif (!part) return undefined;\n\treturn {\n\t\tartifactId: String(part.artifactId || \"artifact\"),\n\t\tblobKey: typeof part.blobKey === \"string\" ? part.blobKey : undefined,\n\t\tname: typeof part.name === \"string\" ? part.name : undefined,\n\t\ttitle: typeof part.title === \"string\" ? part.title : undefined,\n\t\tmimeType: typeof part.mimeType === \"string\" ? part.mimeType : undefined,\n\t\turi: typeof part.uri === \"string\" ? part.uri : undefined,\n\t\tsizeBytes: typeof part.sizeBytes === \"number\" ? part.sizeBytes : undefined,\n\t};\n}\n\nfunction artifactHasPayload(artifact: ArtifactRef): boolean {\n\treturn !!(artifact.uri || artifact.blobKey);\n}\n\nexport function renderScheduledFinalMessage(requestText: string | undefined, responseText: string): string {\n\tconst request = requestText?.trim();\n\tif (!request) return responseText;\n\n\tconst quotedRequest = request\n\t\t.split(/\\r?\\n/)\n\t\t.map((line) => `> ${escapeSlackText(line)}`)\n\t\t.join(\"\\n\");\n\n\treturn [\"*Request:*\", quotedRequest, \"*Antwort:*\", responseText].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction escapeSlackText(text: string): string {\n\treturn text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n}\n\nfunction renderParts(parts: ItemPartLike[]): string {\n\treturn parts\n\t\t.map((part) => {\n\t\t\tif (part.kind === \"text\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"status\") return String(part.status || \"\");\n\t\t\tif (part.kind === \"artifactRef\") {\n\t\t\t\treturn `Artifact: ${String(part.title || part.name || part.artifactId || \"artifact\")}`;\n\t\t\t}\n\t\t\tif (part.kind === \"approval\" || part.kind === \"choice\") {\n\t\t\t\treturn `${String(part.title || \"\")}\\n${String(part.summary || \"\")}`.trim();\n\t\t\t}\n\t\t\tif (part.kind === \"form\") return String(part.title || \"\");\n\t\t\tif (part.kind === \"log\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"patch\" || part.kind === \"diff\") {\n\t\t\t\tconst files = Array.isArray(part.files) ? (part.files as Array<{ path?: unknown }>) : [];\n\t\t\t\treturn files.map((entry) => `File: ${String(entry.path || \"\")}`).join(\"\\n\");\n\t\t\t}\n\t\t\treturn JSON.stringify(part);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction validateScheduledRunConfig(config: SlackScheduledRunConfig, source: string): void {\n\tif (!config.id) throw new Error(`Missing id in ${source}`);\n\tif (!config.routeId) throw new Error(`Missing routeId in ${source}`);\n\tif (!config.text) throw new Error(`Missing text in ${source}`);\n\tif (!config.target) throw new Error(`Missing target in ${source}`);\n\tif (!config.target.slackUserId && !config.target.channelId) {\n\t\tthrow new Error(`Missing target.slackUserId or target.channelId in ${source}`);\n\t}\n}\n\nfunction asRunFailedPayload(event: BeeRunEvent): RunFailedPayload {\n\treturn event.payload as RunFailedPayload;\n}\n\nfunction asApprovalRequestedPayload(event: BeeRunEvent): ApprovalRequestedPayload {\n\treturn event.payload as ApprovalRequestedPayload;\n}\n\nfunction asItemAppendedPayload(event: BeeRunEvent): ItemAppendedPayload {\n\treturn event.payload as ItemAppendedPayload;\n}\n\nfunction asItemUpdatedPayload(event: BeeRunEvent): ItemUpdatedPayload {\n\treturn event.payload as ItemUpdatedPayload;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobmatchme/bee-slack",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Slack adapter for Bee Gate and the Bee Dance protocol",
5
5
  "type": "module",
6
6
  "bin": {