(
initialResume?: { resume: unknown },
extra?: {
context?: ContextEntry[];
tools?: BotTool[];
prompt?: string | AgentContentPart[];
transcript?: boolean | { limit?: number };
},
)
| 317 | } |
| 318 | |
| 319 | private async run( |
| 320 | initialResume?: { resume: unknown }, |
| 321 | extra?: { |
| 322 | context?: ContextEntry[]; |
| 323 | tools?: BotTool[]; |
| 324 | prompt?: string | AgentContentPart[]; |
| 325 | transcript?: boolean | { limit?: number }; |
| 326 | }, |
| 327 | ): Promise<MessageRef | undefined> { |
| 328 | const session = await this.deps.adapter.conversationStore.getOrCreate( |
| 329 | this.deps.conversationKey, |
| 330 | this.deps.replyTarget, |
| 331 | this.deps.agentFactory, |
| 332 | ); |
| 333 | // Inject an explicit user message when the input isn't in the adapter's |
| 334 | // reconstructed history (e.g. a slash command's args, or inbound image/file |
| 335 | // attachments built into multimodal content parts). A non-empty array is |
| 336 | // truthy, so this guard also admits multimodal prompts. |
| 337 | if (extra?.prompt) { |
| 338 | session.agent.addMessage({ |
| 339 | id: globalThis.crypto.randomUUID(), |
| 340 | role: "user", |
| 341 | // AG-UI types `content` as `string`, but multimodal works at runtime by |
| 342 | // setting it to an `AgentContentPart[]` — the runtime's LLM adapter |
| 343 | // converts the parts to the provider's multimodal format. We cast to |
| 344 | // satisfy the string-typed field (bot-slack parity — it does the same |
| 345 | // when assigning multimodal `content` to its reconstructed messages). |
| 346 | content: extra.prompt as unknown as string, |
| 347 | }); |
| 348 | } |
| 349 | const renderer = this.deps.adapter.createRunRenderer(this.deps.replyTarget); |
| 350 | |
| 351 | // Transcript auto-bridge (step 1 + 2): inject prior cross-platform history |
| 352 | // as a context entry, then append the current user turn. This flag owns the |
| 353 | // bridge — see `runAgent`'s `transcript` doc. No-ops with one warning when |
| 354 | // identity/transcripts aren't configured. |
| 355 | const transcripts = this.deps.transcripts; |
| 356 | const userKey = this.deps.userKey; |
| 357 | let transcriptContext: ContextEntry | undefined; |
| 358 | if (extra?.transcript) { |
| 359 | if (transcripts && userKey) { |
| 360 | const limit = |
| 361 | typeof extra.transcript === "object" |
| 362 | ? (extra.transcript.limit ?? 20) |
| 363 | : 20; |
| 364 | // List BEFORE appending the current user turn so the current message |
| 365 | // isn't counted as its own "prior history". |
| 366 | const prior = await transcripts.list({ userKey, limit }); |
| 367 | if (prior.length > 0) { |
| 368 | transcriptContext = { |
| 369 | description: `Prior cross-platform conversation history with this user. Current channel: ${this.platform}.`, |
| 370 | value: prior |
| 371 | .map((e) => `[${e.platform}] ${e.role}: ${e.text}`) |
| 372 | .join("\n"), |
| 373 | }; |
| 374 | } |
| 375 | if (this.deps.message) { |
| 376 | await transcripts.append(this, this.deps.message, { userKey }); |
no test coverage detected