feat: multi-agent routing + multi-account providers
This commit is contained in:
@@ -29,6 +29,8 @@ import type { ClawdbotConfig } from "../config/config.js";
|
||||
import {
|
||||
DEFAULT_IDLE_MINUTES,
|
||||
loadSessionStore,
|
||||
resolveAgentIdFromSessionKey,
|
||||
resolveMainSessionKey,
|
||||
resolveSessionTranscriptPath,
|
||||
resolveStorePath,
|
||||
type SessionEntry,
|
||||
@@ -87,7 +89,7 @@ function isHeartbeatOnlyResponse(
|
||||
function resolveDeliveryTarget(
|
||||
cfg: ClawdbotConfig,
|
||||
jobPayload: {
|
||||
channel?:
|
||||
provider?:
|
||||
| "last"
|
||||
| "whatsapp"
|
||||
| "telegram"
|
||||
@@ -98,36 +100,37 @@ function resolveDeliveryTarget(
|
||||
to?: string;
|
||||
},
|
||||
) {
|
||||
const requestedChannel =
|
||||
typeof jobPayload.channel === "string" ? jobPayload.channel : "last";
|
||||
const requestedProvider =
|
||||
typeof jobPayload.provider === "string" ? jobPayload.provider : "last";
|
||||
const explicitTo =
|
||||
typeof jobPayload.to === "string" && jobPayload.to.trim()
|
||||
? jobPayload.to.trim()
|
||||
: undefined;
|
||||
|
||||
const sessionCfg = cfg.session;
|
||||
const mainKey = (sessionCfg?.mainKey ?? "main").trim() || "main";
|
||||
const storePath = resolveStorePath(sessionCfg?.store);
|
||||
const mainSessionKey = resolveMainSessionKey(cfg);
|
||||
const agentId = resolveAgentIdFromSessionKey(mainSessionKey);
|
||||
const storePath = resolveStorePath(sessionCfg?.store, { agentId });
|
||||
const store = loadSessionStore(storePath);
|
||||
const main = store[mainKey];
|
||||
const lastChannel =
|
||||
main?.lastChannel && main.lastChannel !== "webchat"
|
||||
? main.lastChannel
|
||||
const main = store[mainSessionKey];
|
||||
const lastProvider =
|
||||
main?.lastProvider && main.lastProvider !== "webchat"
|
||||
? main.lastProvider
|
||||
: undefined;
|
||||
const lastTo = typeof main?.lastTo === "string" ? main.lastTo.trim() : "";
|
||||
|
||||
const channel = (() => {
|
||||
const provider = (() => {
|
||||
if (
|
||||
requestedChannel === "whatsapp" ||
|
||||
requestedChannel === "telegram" ||
|
||||
requestedChannel === "discord" ||
|
||||
requestedChannel === "slack" ||
|
||||
requestedChannel === "signal" ||
|
||||
requestedChannel === "imessage"
|
||||
requestedProvider === "whatsapp" ||
|
||||
requestedProvider === "telegram" ||
|
||||
requestedProvider === "discord" ||
|
||||
requestedProvider === "slack" ||
|
||||
requestedProvider === "signal" ||
|
||||
requestedProvider === "imessage"
|
||||
) {
|
||||
return requestedChannel;
|
||||
return requestedProvider;
|
||||
}
|
||||
return lastChannel ?? "whatsapp";
|
||||
return lastProvider ?? "whatsapp";
|
||||
})();
|
||||
|
||||
const to = (() => {
|
||||
@@ -136,7 +139,7 @@ function resolveDeliveryTarget(
|
||||
})();
|
||||
|
||||
const sanitizedWhatsappTo = (() => {
|
||||
if (channel !== "whatsapp") return to;
|
||||
if (provider !== "whatsapp") return to;
|
||||
const rawAllow = cfg.whatsapp?.allowFrom ?? [];
|
||||
if (rawAllow.includes("*")) return to;
|
||||
const allowFrom = rawAllow
|
||||
@@ -150,8 +153,8 @@ function resolveDeliveryTarget(
|
||||
})();
|
||||
|
||||
return {
|
||||
channel,
|
||||
to: channel === "whatsapp" ? sanitizedWhatsappTo : to,
|
||||
provider,
|
||||
to: provider === "whatsapp" ? sanitizedWhatsappTo : to,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -181,7 +184,7 @@ function resolveCronSession(params: {
|
||||
model: entry?.model,
|
||||
contextTokens: entry?.contextTokens,
|
||||
sendPolicy: entry?.sendPolicy,
|
||||
lastChannel: entry?.lastChannel,
|
||||
lastProvider: entry?.lastProvider,
|
||||
lastTo: entry?.lastTo,
|
||||
};
|
||||
return { storePath, store, sessionEntry, systemSent, isNewSession: !fresh };
|
||||
@@ -251,9 +254,9 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
params.job.payload.bestEffortDeliver === true;
|
||||
|
||||
const resolvedDelivery = resolveDeliveryTarget(params.cfg, {
|
||||
channel:
|
||||
provider:
|
||||
params.job.payload.kind === "agentTurn"
|
||||
? params.job.payload.channel
|
||||
? params.job.payload.provider
|
||||
: "last",
|
||||
to:
|
||||
params.job.payload.kind === "agentTurn"
|
||||
@@ -302,7 +305,7 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
registerAgentRunContext(cronSession.sessionEntry.sessionId, {
|
||||
sessionKey: params.sessionKey,
|
||||
});
|
||||
const surface = resolvedDelivery.channel;
|
||||
const messageProvider = resolvedDelivery.provider;
|
||||
const fallbackResult = await runWithModelFallback({
|
||||
cfg: params.cfg,
|
||||
provider,
|
||||
@@ -311,7 +314,7 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
runEmbeddedPiAgent({
|
||||
sessionId: cronSession.sessionEntry.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
surface,
|
||||
messageProvider,
|
||||
sessionFile,
|
||||
workspaceDir,
|
||||
config: params.cfg,
|
||||
@@ -380,7 +383,7 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
delivery && isHeartbeatOnlyResponse(payloads, Math.max(0, ackMaxChars));
|
||||
|
||||
if (delivery && !skipHeartbeatDelivery) {
|
||||
if (resolvedDelivery.channel === "whatsapp") {
|
||||
if (resolvedDelivery.provider === "whatsapp") {
|
||||
if (!resolvedDelivery.to) {
|
||||
if (!bestEffortDeliver)
|
||||
return {
|
||||
@@ -415,7 +418,7 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
return { status: "error", summary, error: String(err) };
|
||||
return { status: "ok", summary };
|
||||
}
|
||||
} else if (resolvedDelivery.channel === "telegram") {
|
||||
} else if (resolvedDelivery.provider === "telegram") {
|
||||
if (!resolvedDelivery.to) {
|
||||
if (!bestEffortDeliver)
|
||||
return {
|
||||
@@ -459,14 +462,14 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
return { status: "error", summary, error: String(err) };
|
||||
return { status: "ok", summary };
|
||||
}
|
||||
} else if (resolvedDelivery.channel === "discord") {
|
||||
} else if (resolvedDelivery.provider === "discord") {
|
||||
if (!resolvedDelivery.to) {
|
||||
if (!bestEffortDeliver)
|
||||
return {
|
||||
status: "error",
|
||||
summary,
|
||||
error:
|
||||
"Cron delivery to Discord requires --channel discord and --to <channelId|user:ID>",
|
||||
"Cron delivery to Discord requires --provider discord and --to <channelId|user:ID>",
|
||||
};
|
||||
return {
|
||||
status: "skipped",
|
||||
@@ -503,14 +506,14 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
return { status: "error", summary, error: String(err) };
|
||||
return { status: "ok", summary };
|
||||
}
|
||||
} else if (resolvedDelivery.channel === "slack") {
|
||||
} else if (resolvedDelivery.provider === "slack") {
|
||||
if (!resolvedDelivery.to) {
|
||||
if (!bestEffortDeliver)
|
||||
return {
|
||||
status: "error",
|
||||
summary,
|
||||
error:
|
||||
"Cron delivery to Slack requires --channel slack and --to <channelId|user:ID>",
|
||||
"Cron delivery to Slack requires --provider slack and --to <channelId|user:ID>",
|
||||
};
|
||||
return {
|
||||
status: "skipped",
|
||||
@@ -543,7 +546,7 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
return { status: "error", summary, error: String(err) };
|
||||
return { status: "ok", summary };
|
||||
}
|
||||
} else if (resolvedDelivery.channel === "signal") {
|
||||
} else if (resolvedDelivery.provider === "signal") {
|
||||
if (!resolvedDelivery.to) {
|
||||
if (!bestEffortDeliver)
|
||||
return {
|
||||
@@ -582,7 +585,7 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
return { status: "error", summary, error: String(err) };
|
||||
return { status: "ok", summary };
|
||||
}
|
||||
} else if (resolvedDelivery.channel === "imessage") {
|
||||
} else if (resolvedDelivery.provider === "imessage") {
|
||||
if (!resolvedDelivery.to) {
|
||||
if (!bestEffortDeliver)
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user