UNPKG

@mastra/core

Version:
167 lines (166 loc) 6.57 kB
const require_agent = require("./agent-DCD4MApC.cjs"); //#region src/channels/agent-controller-channels.ts /** * Runs an AgentController inside chat channels (Slack, Discord, ...). * * Extends {@link AgentChannels} so all inbound machinery (thread mapping, * history, attachments, event context) and all outbound rendering * (`ChatChannelOutputProcessor` with native streaming, tool cards, typing * status) are reused unchanged. Only the dispatch seams differ: instead of * routing into a bare agent, inbound messages route into a controller * `Session` — one durable session per chat thread, keyed by the mapped * Mastra thread's `resourceId`. * * V1 targets long-lived servers: controller sessions are in-memory objects * and do not survive process restarts. */ var AgentControllerChannels = class extends require_agent.AgentChannels { controller = null; /** * Session resourceIds whose adapter can't render approval buttons, so their * runs must auto-approve tools (`requireToolApproval: false`) instead of * parking forever on an approval nobody can answer. Kept outside session * state on purpose: state is validated against the controller's * `stateSchema`, which would strip (or reject) an injected flag. Refreshed * on every inbound message; in-memory only, matching the v1 long-lived * server scope. */ autoApproveResourceIds = /* @__PURE__ */ new Set(); /** @internal Called by AgentController's constructor to bind itself. */ __setController(controller) { this.controller = controller; } /** * @internal Consulted by the controller's run-option builder: `true` when * the session's channel adapter can't render approval buttons and tool * calls must auto-approve (the session-side equivalent of the base agent * path's `autoResumeSuspendedTools`). */ __isAutoApproveResource(resourceId) { return this.autoApproveResourceIds.has(resourceId); } /** * @internal No-op override. The controller attaches this instance to its * mode agents via `Agent.setChannels`, which calls `__setAgent(agent)` — * with multiple mode agents the last one would win. Every `this.agent` use * is overridden in this subclass, so keep the base field unset rather than * holding a misleading ref. */ __setAgent(_agent) {} getOwnerId() { return this.controller?.id ?? null; } getWebhookBasePath() { return `/api/agent-controllers/${this.getOwnerId()}`; } getMastra() { return this.controller?.getMastra(); } /** * One session per chat thread: unless the user supplied a custom * `resolveResourceId`, key new Mastra threads (and therefore controller * sessions) off the platform + external thread id. */ resolveChannelResourceId(args) { const base = super.resolveChannelResourceId(args); if (typeof base === "function") return base; return `channel:${args.chatThread.id}`; } /** * Route an inbound chat message into the controller session bound to this * chat thread. Output renders back to the platform through the channels * output processor: the `requestContext` built by the base class (carrying * the channel context and render context) flows through the session into * the run. */ async dispatchInboundMessage(args) { const { signalContents, attributes, providerOptions, requestContext, thread, autoResumeSuspendedTools } = args; const session = await this.getSessionForThread(thread, requestContext); const sessionResourceId = thread.resourceId; if (autoResumeSuspendedTools) this.autoApproveResourceIds.add(sessionResourceId); else this.autoApproveResourceIds.delete(sessionResourceId); await session.sendSignal({ content: signalContents, ifActive: { attributes }, ifIdle: { attributes }, requestContext, providerOptions }).accepted; } /** * Resolve an approval-card "approve" action against the controller session's * parked tool-approval gate. The run engine — awaiting the gate inside its * stream-consumer loop — performs the actual resume itself and keeps * consuming, so the continuation renders through the output processor. */ async dispatchApproval(args) { await this.respondToSessionApproval({ decision: "approve", ...args }); } /** * Resolve an approval-card "deny" action against the controller session's * parked tool-approval gate (see {@link dispatchApproval}). */ async dispatchDecline(args) { await this.respondToSessionApproval({ decision: "decline", ...args }); } /** * Shared approve/decline path. Never calls the session's internal * `approveToolCall`/`declineToolCall` executors directly — the engine parked * at the gate owns the resume. `respondToToolApproval` is a silent no-op * when nothing is armed or the toolCallId mismatches, so staleness is * pre-checked explicitly (an armed gate does not survive process restarts, * so restart-recovered approvals are always stale — consistent with the * v1 long-lived-server scope). */ async respondToSessionApproval({ decision, toolCallId, requestContext, memory }) { const session = await this.getSessionForThread({ id: memory.thread, resourceId: memory.resource }); if (!session.approval.isArmed() || session.approval.getToolCallId() !== toolCallId) { this.log("info", `Ignoring stale tool ${decision === "approve" ? "approval" : "denial"} action (no matching parked approval for toolCallId=${toolCallId})`); return; } session.respondToToolApproval({ decision, toolCallId, requestContext }); } /** * Get-or-create the durable controller session for a mapped channel thread * and bind it to that thread. Keyed off the thread's own `resourceId` so * pre-existing threads (custom resolveResourceId, or created before this * feature) always pass the session's thread-ownership check. */ async getSessionForThread(thread, requestContext) { const controller = this.requireController(); const channelResourceId = thread.resourceId; const session = await controller.createSession({ resourceId: channelResourceId, id: channelResourceId, ownerId: controller.id, requestContext }); if (session.thread.getId() !== thread.id) await session.thread.switch({ threadId: thread.id }); return session; } requireController() { if (!this.controller) throw new Error("AgentControllerChannels is not bound to an AgentController. Pass it via `channels` in AgentControllerConfig."); return this.controller; } }; //#endregion Object.defineProperty(exports, "AgentControllerChannels", { enumerable: true, get: function() { return AgentControllerChannels; } }); //# sourceMappingURL=channels-DpBkVl9k.cjs.map