Skip to content

Commit 76ae8c4

Browse files
committed
fix(agents): wait for cron media tasks
1 parent 6bd065d commit 76ae8c4

24 files changed

Lines changed: 1027 additions & 272 deletions

extensions/discord/src/channel.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,10 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
394394
token: account.token,
395395
inputs,
396396
missingTokenNote: "missing Discord token",
397-
resolveWithToken: async ({ token, inputs }) =>
397+
resolveWithToken: async ({ token, inputs: resolvedInputs }) =>
398398
(await loadDiscordResolveChannelsModule()).resolveDiscordChannelAllowlist({
399399
token,
400-
entries: inputs,
400+
entries: resolvedInputs,
401401
}),
402402
mapResolved: (entry) => ({
403403
input: entry.input,
@@ -415,10 +415,10 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
415415
token: account.token,
416416
inputs,
417417
missingTokenNote: "missing Discord token",
418-
resolveWithToken: async ({ token, inputs }) =>
418+
resolveWithToken: async ({ token, inputs: resolvedInputs }) =>
419419
(await loadDiscordResolveUsersModule()).resolveDiscordUserAllowlist({
420420
token,
421-
entries: inputs,
421+
entries: resolvedInputs,
422422
}),
423423
mapResolved: (entry) => ({
424424
input: entry.input,

extensions/google/transport-stream.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ const {
1010
googleAuthGetAccessTokenMock,
1111
googleAuthMock,
1212
} = vi.hoisted(() => {
13-
const googleAuthGetAccessTokenMock = vi.fn();
13+
const getAccessTokenMock = vi.fn();
1414
return {
1515
buildGuardedModelFetchMock: vi.fn(),
1616
guardedFetchMock: vi.fn(),
17-
googleAuthGetAccessTokenMock,
17+
googleAuthGetAccessTokenMock: getAccessTokenMock,
1818
googleAuthMock: vi.fn(function GoogleAuthMock() {
1919
return {
20-
getAccessToken: googleAuthGetAccessTokenMock,
20+
getAccessToken: getAccessTokenMock,
2121
};
2222
}),
2323
};

extensions/imessage/src/send.test.ts

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import {
1414
import { hasPersistedIMessageEcho } from "./monitor/persisted-echo-cache.js";
1515
import { sendMessageIMessage } from "./send.js";
1616

17+
type TestIMessageRpcClient = IMessageRpcClient & {
18+
requestMock: ReturnType<typeof vi.fn>;
19+
stopMock: ReturnType<typeof vi.fn>;
20+
};
21+
1722
const IMESSAGE_TEST_CFG = {
1823
channels: {
1924
imessage: {
@@ -24,21 +29,29 @@ const IMESSAGE_TEST_CFG = {
2429
},
2530
};
2631

27-
function createClient(result: Record<string, unknown>): IMessageRpcClient {
32+
function createClient(result: Record<string, unknown>): TestIMessageRpcClient {
33+
const requestMock = vi.fn(async () => result);
34+
const stopMock = vi.fn(async () => {});
2835
return {
29-
request: vi.fn(async () => result),
30-
stop: vi.fn(async () => {}),
31-
} as unknown as IMessageRpcClient;
36+
request: requestMock,
37+
stop: stopMock,
38+
requestMock,
39+
stopMock,
40+
} as unknown as TestIMessageRpcClient;
3241
}
3342

34-
function createRejectingClient(error: Error): IMessageRpcClient {
43+
function createRejectingClient(error: Error): TestIMessageRpcClient {
44+
const requestMock = vi.fn(async () => {
45+
await Promise.resolve();
46+
throw error;
47+
});
48+
const stopMock = vi.fn(async () => {});
3549
return {
36-
request: vi.fn(async () => {
37-
await Promise.resolve();
38-
throw error;
39-
}),
40-
stop: vi.fn(async () => {}),
41-
} as unknown as IMessageRpcClient;
50+
request: requestMock,
51+
stop: stopMock,
52+
requestMock,
53+
stopMock,
54+
} as unknown as TestIMessageRpcClient;
4255
}
4356

4457
function createApprovalText(id = "approval-123"): string {
@@ -118,7 +131,7 @@ describe("sendMessageIMessage receipts", () => {
118131
expect(result.echoText).toBe("<media:image>");
119132
expect(result.receipt.primaryPlatformMessageId).toBe("p:0/media-guid");
120133
expect(result.receipt.platformMessageIds).toEqual(["p:0/media-guid"]);
121-
expect(client.request).not.toHaveBeenCalled();
134+
expect(client.requestMock).not.toHaveBeenCalled();
122135
expect(runCliJson.mock.calls).toEqual([
123136
[["send-attachment", "--chat", "chat-1", "--file", "/tmp/image.png", "--transport", "auto"]],
124137
]);
@@ -162,7 +175,7 @@ describe("sendMessageIMessage receipts", () => {
162175
});
163176

164177
expect(result.messageId).toBe("p:0/media-guid");
165-
expect(client.request).not.toHaveBeenCalled();
178+
expect(client.requestMock).not.toHaveBeenCalled();
166179
expect(runCliJson.mock.calls).toEqual([
167180
[["group", "--chat-id", "42"]],
168181
[
@@ -208,7 +221,7 @@ describe("sendMessageIMessage receipts", () => {
208221
expect(runCliJson.mock.calls).toEqual([
209222
[["send-attachment", "--chat", "chat-1", "--file", "/tmp/image.png", "--transport", "auto"]],
210223
]);
211-
expect(client.request).toHaveBeenCalledWith(
224+
expect(client.requestMock).toHaveBeenCalledWith(
212225
"send",
213226
expect.objectContaining({
214227
chat_guid: "chat-1",
@@ -233,7 +246,7 @@ describe("sendMessageIMessage receipts", () => {
233246

234247
expect(result.messageId).toBe("12345");
235248
expect(runCliJson.mock.calls).toEqual([[["group", "--chat-id", "42"]]]);
236-
expect(client.request).toHaveBeenCalledWith(
249+
expect(client.requestMock).toHaveBeenCalledWith(
237250
"send",
238251
expect.objectContaining({
239252
chat_id: 42,
@@ -259,7 +272,7 @@ describe("sendMessageIMessage receipts", () => {
259272
runCliJson,
260273
}),
261274
).rejects.toThrow("attachment delivery failed");
262-
expect(client.request).not.toHaveBeenCalled();
275+
expect(client.requestMock).not.toHaveBeenCalled();
263276
});
264277

265278
it("routes DM handle media-only sends through send-attachment", async () => {
@@ -294,7 +307,7 @@ describe("sendMessageIMessage receipts", () => {
294307
isFromMe: true,
295308
}),
296309
);
297-
expect(client.request).not.toHaveBeenCalled();
310+
expect(client.requestMock).not.toHaveBeenCalled();
298311
});
299312

300313
it("preserves explicit SMS service for bare-handle media sends", async () => {
@@ -319,7 +332,7 @@ describe("sendMessageIMessage receipts", () => {
319332
"--transport",
320333
"auto",
321334
]);
322-
expect(client.request).not.toHaveBeenCalled();
335+
expect(client.requestMock).not.toHaveBeenCalled();
323336
});
324337

325338
it("preserves configured iMessage service for bare-handle media sends", async () => {
@@ -353,7 +366,7 @@ describe("sendMessageIMessage receipts", () => {
353366
"--transport",
354367
"auto",
355368
]);
356-
expect(client.request).not.toHaveBeenCalled();
369+
expect(client.requestMock).not.toHaveBeenCalled();
357370
});
358371

359372
it("keeps national-format phone media sends on the region-aware RPC path", async () => {
@@ -370,7 +383,7 @@ describe("sendMessageIMessage receipts", () => {
370383
});
371384

372385
expect(runCliJson).not.toHaveBeenCalled();
373-
expect(client.request).toHaveBeenCalledWith(
386+
expect(client.requestMock).toHaveBeenCalledWith(
374387
"send",
375388
expect.objectContaining({
376389
file: "/tmp/image.png",
@@ -395,7 +408,7 @@ describe("sendMessageIMessage receipts", () => {
395408
});
396409

397410
expect(runCliJson).not.toHaveBeenCalled();
398-
expect(client.request).toHaveBeenCalledWith(
411+
expect(client.requestMock).toHaveBeenCalledWith(
399412
"send",
400413
expect.objectContaining({
401414
chat_identifier: "team-thread",
@@ -428,7 +441,7 @@ describe("sendMessageIMessage receipts", () => {
428441
"--transport",
429442
"auto",
430443
]);
431-
expect(client.request).toHaveBeenCalledWith(
444+
expect(client.requestMock).toHaveBeenCalledWith(
432445
"send",
433446
expect.objectContaining({
434447
to: "+15550004567",
@@ -495,7 +508,7 @@ describe("sendMessageIMessage receipts", () => {
495508
expect(runCliJson.mock.calls).toEqual([
496509
[["send-attachment", "--chat", "chat-1", "--file", "/tmp/image.png", "--transport", "auto"]],
497510
]);
498-
expect(client.request).toHaveBeenCalledWith(
511+
expect(client.requestMock).toHaveBeenCalledWith(
499512
"send",
500513
expect.objectContaining({
501514
chat_guid: "chat-1",
@@ -522,15 +535,15 @@ describe("sendMessageIMessage receipts", () => {
522535
});
523536

524537
expect(createClientImpl).toHaveBeenCalledTimes(1);
525-
expect(createdClient.request).toHaveBeenCalledWith(
538+
expect(createdClient.requestMock).toHaveBeenCalledWith(
526539
"send",
527540
expect.objectContaining({
528541
to: "+15550004567",
529542
text: "caption",
530543
}),
531544
expect.any(Object),
532545
);
533-
expect(createdClient.stop).toHaveBeenCalledOnce();
546+
expect(createdClient.stopMock).toHaveBeenCalledOnce();
534547
expect(result.receipt.platformMessageIds).toEqual(["p:0/dm-media-guid", "p:0/caption-guid"]);
535548
expect(result.receipt.parts.map((part) => part.kind)).toEqual(["media", "text"]);
536549
});
@@ -545,7 +558,7 @@ describe("sendMessageIMessage receipts", () => {
545558

546559
expect(result.sentText).toBe("literal <media:image> text");
547560
expect(result.echoText).toBe("literal <media:image> text");
548-
expect(client.request).toHaveBeenCalledWith(
561+
expect(client.requestMock).toHaveBeenCalledWith(
549562
"send",
550563
expect.objectContaining({
551564
chat_id: 42,
@@ -619,14 +632,14 @@ describe("sendMessageIMessage receipts", () => {
619632

620633
it("recovers approval prompt GUID without resending when rpc send times out", async () => {
621634
const client = createRejectingClient(new Error("imsg rpc timeout (send)"));
622-
const createClient = vi.fn(async () => client);
635+
const createClientImpl = vi.fn(async () => client);
623636
const runCliJson = vi.fn();
624637
const resolveSentMessageGuidImpl = vi.fn(async () => "p:0/fallback-guid");
625638
const approvalText = createApprovalText();
626639

627640
const result = await sendMessageIMessage("chat_id:42", approvalText, {
628641
config: IMESSAGE_TEST_CFG,
629-
createClient,
642+
createClient: createClientImpl,
630643
runCliJson,
631644
service: "sms",
632645
dbPath: "/Users/me/Library/Messages/chat.db",
@@ -635,7 +648,7 @@ describe("sendMessageIMessage receipts", () => {
635648

636649
expect(result.messageId).toBe("p:0/fallback-guid");
637650
expect(result.guid).toBe("p:0/fallback-guid");
638-
expect(client.stop).toHaveBeenCalledOnce();
651+
expect(client.stopMock).toHaveBeenCalledOnce();
639652
expect(runCliJson).not.toHaveBeenCalled();
640653
expect(resolveSentMessageGuidImpl).toHaveBeenCalledWith({
641654
dbPath: "/Users/me/Library/Messages/chat.db",

extensions/slack/src/monitor/message-handler/dispatch.preview-fallback.test.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -244,16 +244,6 @@ function expectDeliverReplyCall(index: number, text: string, fields?: Record<str
244244
const noop = () => {};
245245
const noopAsync = async () => {};
246246

247-
function createDeferred<T>() {
248-
let resolve!: (value: T) => void;
249-
let reject!: (reason?: unknown) => void;
250-
const promise = new Promise<T>((promiseResolve, promiseReject) => {
251-
resolve = promiseResolve;
252-
reject = promiseReject;
253-
});
254-
return { promise, resolve, reject };
255-
}
256-
257247
function createDraftStreamStub() {
258248
return {
259249
update: vi.fn(),

extensions/slack/src/monitor/message-handler/dispatch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
635635
},
636636
onStartError: (err) => {
637637
logTypingFailure({
638-
log: (message) => runtime.error?.(danger(message)),
638+
log: (logMessage) => runtime.error?.(danger(logMessage)),
639639
channel: "slack",
640640
action: "start",
641641
target: typingTarget,
@@ -644,7 +644,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
644644
},
645645
onStopError: (err) => {
646646
logTypingFailure({
647-
log: (message) => runtime.error?.(danger(message)),
647+
log: (logMessage) => runtime.error?.(danger(logMessage)),
648648
channel: "slack",
649649
action: "stop",
650650
target: typingTarget,

packages/terminal-core/src/table.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ function wrapLine(text: string, width: number): string[] {
187187
return params.every((param) => Number.isInteger(param)) ? params : null;
188188
};
189189

190-
const activeSgrAfter = (tokens: Token[]) => {
190+
const activeSgrAfter = (lineTokens: Token[]) => {
191191
type SgrCategory =
192192
| "background"
193193
| "blink"
@@ -272,7 +272,7 @@ function wrapLine(text: string, width: number): string[] {
272272
}
273273
return false;
274274
};
275-
for (const token of tokens) {
275+
for (const token of lineTokens) {
276276
if (token.kind !== "ansi") {
277277
continue;
278278
}
@@ -314,17 +314,17 @@ function wrapLine(text: string, width: number): string[] {
314314
lines.push(cleaned);
315315
};
316316

317-
const trimLeadingSpaces = (tokens: Token[]) => {
317+
const trimLeadingSpaces = (lineTokens: Token[]) => {
318318
while (true) {
319-
const firstCharIndex = tokens.findIndex((token) => token.kind === "char");
320-
if (firstCharIndex < 0) {
319+
const firstLineCharIndex = lineTokens.findIndex((token) => token.kind === "char");
320+
if (firstLineCharIndex < 0) {
321321
return;
322322
}
323-
const firstChar = tokens[firstCharIndex];
323+
const firstChar = lineTokens[firstLineCharIndex];
324324
if (!firstChar || !isSpaceChar(firstChar.value)) {
325325
return;
326326
}
327-
tokens.splice(firstCharIndex, 1);
327+
lineTokens.splice(firstLineCharIndex, 1);
328328
}
329329
};
330330

@@ -513,8 +513,8 @@ export function renderTable(opts: RenderTableOptions): string {
513513
// If we have room and any flex columns, expand them to fill the available width.
514514
// This keeps tables from looking "clipped" and reduces wrapping in wide terminals.
515515
if (maxWidth) {
516-
const sepCount = columns.length + 1;
517-
const currentTotal = widths.reduce((a, b) => a + b, 0) + sepCount;
516+
const separatorCount = columns.length + 1;
517+
const currentTotal = widths.reduce((a, b) => a + b, 0) + separatorCount;
518518
let extra = maxWidth - currentTotal;
519519
if (extra > 0) {
520520
const flexCols = columns

src/agents/embedded-agent-runner/run.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,13 @@ function createScopedAuthProfileStore(
376376
}
377377

378378
function buildTraceToolSummary(params: {
379-
toolMetas?: Array<{ toolName: string; meta?: string; asyncStarted?: boolean }>;
379+
toolMetas?: Array<{
380+
toolName: string;
381+
meta?: string;
382+
asyncStarted?: boolean;
383+
asyncTaskRunId?: string;
384+
asyncTaskId?: string;
385+
}>;
380386
hadFailure: boolean;
381387
}): ToolSummaryTrace | undefined {
382388
if (!params.toolMetas?.length) {
@@ -1245,7 +1251,7 @@ export async function runEmbeddedAgent(
12451251
nextAttemptPromptOverride = MID_TURN_PRECHECK_CONTINUATION_PROMPT;
12461252
suppressNextUserMessagePersistence = true;
12471253
};
1248-
const maybeEscalateRateLimitProfileFallback = (params: {
1254+
const maybeEscalateRateLimitProfileFallback = (fallbackParams: {
12491255
failoverProvider: string;
12501256
failoverModel: string;
12511257
logFallbackDecision: (decision: "fallback_model", extra?: { status?: number }) => void;
@@ -1258,13 +1264,13 @@ export async function runEmbeddedAgent(
12581264
log.warn(
12591265
`rate-limit profile rotation cap reached for ${sanitizeForLog(provider)}/${sanitizeForLog(modelId)} after ${rateLimitProfileRotations} rotations; escalating to model fallback`,
12601266
);
1261-
params.logFallbackDecision("fallback_model", { status });
1267+
fallbackParams.logFallbackDecision("fallback_model", { status });
12621268
throw new FailoverError(
12631269
"The AI service is temporarily rate-limited. Please try again in a moment.",
12641270
{
12651271
reason: "rate_limit",
1266-
provider: params.failoverProvider,
1267-
model: params.failoverModel,
1272+
provider: fallbackParams.failoverProvider,
1273+
model: fallbackParams.failoverModel,
12681274
profileId: lastProfileId,
12691275
sessionId: activeSessionId,
12701276
lane: globalLane,
@@ -3614,9 +3620,9 @@ export async function runEmbeddedAgent(
36143620
step: "bundle-mcp-retire",
36153621
log,
36163622
cleanup: async () => {
3617-
const onError = (error: unknown, sessionId: string) => {
3623+
const onError = (cleanupError: unknown, sessionId: string) => {
36183624
log.warn(
3619-
`bundle-mcp cleanup failed after run for ${sessionId}: ${formatErrorMessage(error)}`,
3625+
`bundle-mcp cleanup failed after run for ${sessionId}: ${formatErrorMessage(cleanupError)}`,
36203626
);
36213627
};
36223628
const retiredBySessionKey = await retireSessionMcpRuntimeForSessionKey({

0 commit comments

Comments
 (0)