mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-20 12:04:35 +00:00
fix(mom): handle Slack message length limits and API errors gracefully
- Truncate messages exceeding Slack's 40,000 char limit - Catch Slack API errors in queue and post to thread instead of crashing - Add error context to queue operations for better debugging - Wrap replaceMessage in try/catch
This commit is contained in:
parent
0e95592eb7
commit
4f845cdd1b
1 changed files with 37 additions and 16 deletions
|
|
@ -377,16 +377,32 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
||||||
// Track stop reason
|
// Track stop reason
|
||||||
let stopReason = "stop";
|
let stopReason = "stop";
|
||||||
|
|
||||||
|
// Slack message limit is 40,000 characters
|
||||||
|
const SLACK_MAX_LENGTH = 40000;
|
||||||
|
const truncateForSlack = (text: string): string => {
|
||||||
|
if (text.length <= SLACK_MAX_LENGTH) return text;
|
||||||
|
return text.substring(0, SLACK_MAX_LENGTH - 100) + "\n\n_(truncated - message too long)_";
|
||||||
|
};
|
||||||
|
|
||||||
// Promise queue to ensure ctx.respond/respondInThread calls execute in order
|
// Promise queue to ensure ctx.respond/respondInThread calls execute in order
|
||||||
|
// Handles errors gracefully by posting to thread instead of crashing
|
||||||
const queue = {
|
const queue = {
|
||||||
chain: Promise.resolve(),
|
chain: Promise.resolve(),
|
||||||
enqueue<T>(fn: () => Promise<T>): Promise<T> {
|
enqueue(fn: () => Promise<void>, errorContext: string): void {
|
||||||
const result = this.chain.then(fn);
|
this.chain = this.chain.then(async () => {
|
||||||
this.chain = result.then(
|
try {
|
||||||
() => {},
|
await fn();
|
||||||
() => {},
|
} catch (err) {
|
||||||
); // swallow errors for chain
|
const errMsg = err instanceof Error ? err.message : String(err);
|
||||||
return result;
|
log.logWarning(`Slack API error (${errorContext})`, errMsg);
|
||||||
|
// Try to post error to thread, but don't crash if that fails too
|
||||||
|
try {
|
||||||
|
await ctx.respondInThread(`_Error: ${errMsg}_`);
|
||||||
|
} catch {
|
||||||
|
// Ignore - we tried our best
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
flush(): Promise<void> {
|
flush(): Promise<void> {
|
||||||
return this.chain;
|
return this.chain;
|
||||||
|
|
@ -421,7 +437,7 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show label in main message only
|
// Show label in main message only
|
||||||
queue.enqueue(() => ctx.respond(`_→ ${label}_`));
|
queue.enqueue(() => ctx.respond(`_→ ${label}_`), "tool label");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -469,11 +485,11 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
||||||
|
|
||||||
threadMessage += "*Result:*\n```\n" + threadResult + "\n```";
|
threadMessage += "*Result:*\n```\n" + threadResult + "\n```";
|
||||||
|
|
||||||
queue.enqueue(() => ctx.respondInThread(threadMessage));
|
queue.enqueue(() => ctx.respondInThread(truncateForSlack(threadMessage)), "tool result thread");
|
||||||
|
|
||||||
// Show brief error in main message if failed
|
// Show brief error in main message if failed
|
||||||
if (event.isError) {
|
if (event.isError) {
|
||||||
queue.enqueue(() => ctx.respond(`_Error: ${truncate(resultStr, 200)}_`));
|
queue.enqueue(() => ctx.respond(`_Error: ${truncate(resultStr, 200)}_`), "tool error");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -528,15 +544,15 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
||||||
// Post thinking to main message and thread
|
// Post thinking to main message and thread
|
||||||
for (const thinking of thinkingParts) {
|
for (const thinking of thinkingParts) {
|
||||||
log.logThinking(logCtx, thinking);
|
log.logThinking(logCtx, thinking);
|
||||||
queue.enqueue(() => ctx.respond(`_${thinking}_`));
|
queue.enqueue(() => ctx.respond(truncateForSlack(`_${thinking}_`)), "thinking main");
|
||||||
queue.enqueue(() => ctx.respondInThread(`_${thinking}_`));
|
queue.enqueue(() => ctx.respondInThread(truncateForSlack(`_${thinking}_`)), "thinking thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post text to main message and thread
|
// Post text to main message and thread
|
||||||
if (text.trim()) {
|
if (text.trim()) {
|
||||||
log.logResponse(logCtx, text);
|
log.logResponse(logCtx, text);
|
||||||
queue.enqueue(() => ctx.respond(text));
|
queue.enqueue(() => ctx.respond(truncateForSlack(text)), "response main");
|
||||||
queue.enqueue(() => ctx.respondInThread(text));
|
queue.enqueue(() => ctx.respondInThread(truncateForSlack(text)), "response thread");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -566,13 +582,18 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
||||||
.map((c) => c.text)
|
.map((c) => c.text)
|
||||||
.join("\n") || "";
|
.join("\n") || "";
|
||||||
if (finalText.trim()) {
|
if (finalText.trim()) {
|
||||||
await ctx.replaceMessage(finalText);
|
try {
|
||||||
|
await ctx.replaceMessage(truncateForSlack(finalText));
|
||||||
|
} catch (err) {
|
||||||
|
const errMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
log.logWarning("Failed to replace message with final text", errMsg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log usage summary if there was any usage
|
// Log usage summary if there was any usage
|
||||||
if (totalUsage.cost.total > 0) {
|
if (totalUsage.cost.total > 0) {
|
||||||
const summary = log.logUsageSummary(logCtx, totalUsage);
|
const summary = log.logUsageSummary(logCtx, totalUsage);
|
||||||
queue.enqueue(() => ctx.respondInThread(summary));
|
queue.enqueue(() => ctx.respondInThread(summary), "usage summary");
|
||||||
await queue.flush();
|
await queue.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue