fix: thread accountId through subagent announce
Co-authored-by: Adam Holt <adam91holt@users.noreply.github.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
- Health: add per-agent session summaries and account-level health details, and allow selective probes. (#1047) — thanks @gumadeiras.
|
||||
|
||||
### Fixes
|
||||
- Sub-agents: route announce delivery through the correct channel account IDs. (#1061, #1058) — thanks @adam91holt.
|
||||
- Messages: `/stop` now hard-aborts queued followups and sub-agent runs; suppress zero-count stop notes.
|
||||
- Sessions: reset `compactionCount` on `/new` and `/reset`, and preserve `sessions.json` file mode (0600).
|
||||
- Sessions: repair orphaned user turns before embedded prompts.
|
||||
|
||||
@@ -108,6 +108,7 @@ export function createClawdbotTools(options?: {
|
||||
createSessionsSpawnTool({
|
||||
agentSessionKey: options?.agentSessionKey,
|
||||
agentChannel: options?.agentChannel,
|
||||
agentAccountId: options?.agentAccountId,
|
||||
sandboxed: options?.sandboxed,
|
||||
}),
|
||||
createSessionStatusTool({
|
||||
|
||||
@@ -306,6 +306,9 @@ async function sendAnnounce(item: AnnounceQueueItem) {
|
||||
params: {
|
||||
sessionKey: item.sessionKey,
|
||||
message: item.prompt,
|
||||
channel: item.originatingChannel,
|
||||
accountId: item.originatingAccountId,
|
||||
to: item.originatingTo,
|
||||
deliver: true,
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
},
|
||||
@@ -348,6 +351,7 @@ async function maybeQueueSubagentAnnounce(params: {
|
||||
requesterSessionKey: string;
|
||||
triggerMessage: string;
|
||||
summaryLine?: string;
|
||||
requesterAccountId?: string;
|
||||
}): Promise<"steered" | "queued" | "none"> {
|
||||
const { cfg, entry } = loadRequesterSessionEntry(params.requesterSessionKey);
|
||||
const canonicalKey = resolveRequesterStoreKey(cfg, params.requesterSessionKey);
|
||||
@@ -382,7 +386,7 @@ async function maybeQueueSubagentAnnounce(params: {
|
||||
sessionKey: canonicalKey,
|
||||
originatingChannel: entry?.lastChannel,
|
||||
originatingTo: entry?.lastTo,
|
||||
originatingAccountId: entry?.lastAccountId,
|
||||
originatingAccountId: entry?.lastAccountId ?? params.requesterAccountId,
|
||||
},
|
||||
queueSettings,
|
||||
);
|
||||
@@ -505,6 +509,7 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
childRunId: string;
|
||||
requesterSessionKey: string;
|
||||
requesterChannel?: string;
|
||||
requesterAccountId?: string;
|
||||
requesterDisplayKey: string;
|
||||
task: string;
|
||||
timeoutMs: number;
|
||||
@@ -600,6 +605,7 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
requesterSessionKey: params.requesterSessionKey,
|
||||
triggerMessage,
|
||||
summaryLine: taskLabel,
|
||||
requesterAccountId: params.requesterAccountId,
|
||||
});
|
||||
if (queued === "steered") {
|
||||
didAnnounce = true;
|
||||
@@ -617,6 +623,8 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
sessionKey: params.requesterSessionKey,
|
||||
message: triggerMessage,
|
||||
deliver: true,
|
||||
channel: params.requesterChannel,
|
||||
accountId: params.requesterAccountId,
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
},
|
||||
expectFinal: true,
|
||||
|
||||
@@ -13,6 +13,7 @@ export type SubagentRunRecord = {
|
||||
childSessionKey: string;
|
||||
requesterSessionKey: string;
|
||||
requesterChannel?: string;
|
||||
requesterAccountId?: string;
|
||||
requesterDisplayKey: string;
|
||||
task: string;
|
||||
cleanup: "delete" | "keep";
|
||||
@@ -59,6 +60,7 @@ function resumeSubagentRun(runId: string) {
|
||||
childRunId: entry.runId,
|
||||
requesterSessionKey: entry.requesterSessionKey,
|
||||
requesterChannel: entry.requesterChannel,
|
||||
requesterAccountId: entry.requesterAccountId,
|
||||
requesterDisplayKey: entry.requesterDisplayKey,
|
||||
task: entry.task,
|
||||
timeoutMs: 30_000,
|
||||
@@ -199,6 +201,7 @@ function ensureListener() {
|
||||
childRunId: entry.runId,
|
||||
requesterSessionKey: entry.requesterSessionKey,
|
||||
requesterChannel: entry.requesterChannel,
|
||||
requesterAccountId: entry.requesterAccountId,
|
||||
requesterDisplayKey: entry.requesterDisplayKey,
|
||||
task: entry.task,
|
||||
timeoutMs: 30_000,
|
||||
@@ -248,6 +251,7 @@ export function registerSubagentRun(params: {
|
||||
childSessionKey: string;
|
||||
requesterSessionKey: string;
|
||||
requesterChannel?: string;
|
||||
requesterAccountId?: string;
|
||||
requesterDisplayKey: string;
|
||||
task: string;
|
||||
cleanup: "delete" | "keep";
|
||||
@@ -264,6 +268,7 @@ export function registerSubagentRun(params: {
|
||||
childSessionKey: params.childSessionKey,
|
||||
requesterSessionKey: params.requesterSessionKey,
|
||||
requesterChannel: params.requesterChannel,
|
||||
requesterAccountId: params.requesterAccountId,
|
||||
requesterDisplayKey: params.requesterDisplayKey,
|
||||
task: params.task,
|
||||
cleanup: params.cleanup,
|
||||
@@ -318,6 +323,7 @@ async function waitForSubagentCompletion(runId: string, waitTimeoutMs: number) {
|
||||
childRunId: entry.runId,
|
||||
requesterSessionKey: entry.requesterSessionKey,
|
||||
requesterChannel: entry.requesterChannel,
|
||||
requesterAccountId: entry.requesterAccountId,
|
||||
requesterDisplayKey: entry.requesterDisplayKey,
|
||||
task: entry.task,
|
||||
timeoutMs: 30_000,
|
||||
|
||||
@@ -48,6 +48,7 @@ function normalizeModelSelection(value: unknown): string | undefined {
|
||||
export function createSessionsSpawnTool(opts?: {
|
||||
agentSessionKey?: string;
|
||||
agentChannel?: GatewayMessageChannel;
|
||||
agentAccountId?: string;
|
||||
sandboxed?: boolean;
|
||||
}): AnyAgentTool {
|
||||
return {
|
||||
@@ -206,6 +207,7 @@ export function createSessionsSpawnTool(opts?: {
|
||||
childSessionKey,
|
||||
requesterSessionKey: requesterInternalKey,
|
||||
requesterChannel: opts?.agentChannel,
|
||||
requesterAccountId: opts?.agentAccountId,
|
||||
requesterDisplayKey,
|
||||
task,
|
||||
cleanup,
|
||||
|
||||
@@ -48,13 +48,19 @@ export async function deliverAgentCommandResult(params: {
|
||||
|
||||
const targetMode: ChannelOutboundTargetMode =
|
||||
opts.deliveryTargetMode ?? (opts.to ? "explicit" : "implicit");
|
||||
const resolvedAccountId =
|
||||
typeof opts.accountId === "string" && opts.accountId.trim()
|
||||
? opts.accountId.trim()
|
||||
: targetMode === "implicit"
|
||||
? sessionEntry?.lastAccountId
|
||||
: undefined;
|
||||
const resolvedTarget =
|
||||
deliver && isDeliveryChannelKnown && deliveryChannel
|
||||
? resolveOutboundTarget({
|
||||
channel: deliveryChannel,
|
||||
to: opts.to,
|
||||
cfg,
|
||||
accountId: targetMode === "implicit" ? sessionEntry?.lastAccountId : undefined,
|
||||
accountId: resolvedAccountId,
|
||||
mode: targetMode,
|
||||
})
|
||||
: null;
|
||||
@@ -112,6 +118,7 @@ export async function deliverAgentCommandResult(params: {
|
||||
cfg,
|
||||
channel: deliveryChannel,
|
||||
to: deliveryTarget,
|
||||
accountId: resolvedAccountId,
|
||||
payloads: deliveryPayloads,
|
||||
bestEffort: bestEffortDeliver,
|
||||
onError: (err) => logDeliveryError(err),
|
||||
|
||||
@@ -23,6 +23,8 @@ export type AgentCommandOpts = {
|
||||
/** Message channel context (webchat|voicewake|whatsapp|...). */
|
||||
messageChannel?: string;
|
||||
channel?: string; // delivery channel (whatsapp|telegram|...)
|
||||
/** Account ID for multi-account channel routing (e.g., WhatsApp account). */
|
||||
accountId?: string;
|
||||
deliveryTargetMode?: ChannelOutboundTargetMode;
|
||||
bestEffortDeliver?: boolean;
|
||||
abortSignal?: AbortSignal;
|
||||
|
||||
@@ -52,6 +52,7 @@ export const AgentParamsSchema = Type.Object(
|
||||
deliver: Type.Optional(Type.Boolean()),
|
||||
attachments: Type.Optional(Type.Array(Type.Unknown())),
|
||||
channel: Type.Optional(Type.String()),
|
||||
accountId: Type.Optional(Type.String()),
|
||||
timeout: Type.Optional(Type.Integer({ minimum: 0 })),
|
||||
lane: Type.Optional(Type.String()),
|
||||
extraSystemPrompt: Type.Optional(Type.String()),
|
||||
|
||||
@@ -60,6 +60,7 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
content?: unknown;
|
||||
}>;
|
||||
channel?: string;
|
||||
accountId?: string;
|
||||
lane?: string;
|
||||
extraSystemPrompt?: string;
|
||||
idempotencyKey: string;
|
||||
@@ -199,6 +200,10 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
|
||||
const lastChannel = sessionEntry?.lastChannel;
|
||||
const lastTo = typeof sessionEntry?.lastTo === "string" ? sessionEntry.lastTo.trim() : "";
|
||||
const resolvedAccountId =
|
||||
typeof request.accountId === "string" && request.accountId.trim()
|
||||
? request.accountId.trim()
|
||||
: sessionEntry?.lastAccountId;
|
||||
|
||||
const wantsDelivery = request.deliver === true;
|
||||
|
||||
@@ -235,7 +240,7 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
const fallback = resolveOutboundTarget({
|
||||
channel: resolvedChannel,
|
||||
cfg,
|
||||
accountId: sessionEntry?.lastAccountId ?? undefined,
|
||||
accountId: resolvedAccountId,
|
||||
mode: "implicit",
|
||||
});
|
||||
if (fallback.ok) {
|
||||
@@ -269,6 +274,7 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
deliver,
|
||||
deliveryTargetMode,
|
||||
channel: resolvedChannel,
|
||||
accountId: resolvedAccountId,
|
||||
timeout: request.timeout?.toString(),
|
||||
bestEffortDeliver,
|
||||
messageChannel: resolvedChannel,
|
||||
|
||||
Reference in New Issue
Block a user