mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 09:01:14 +00:00
feat(mom): split long messages instead of truncating
- Messages > 40k chars are split into multiple Slack messages - Full tool results now shown in thread (no truncation) - Main message truncates with 'see thread for full response' if too long - Keep jsonl log truncation for storage efficiency
This commit is contained in:
parent
4f845cdd1b
commit
330e044b55
1 changed files with 33 additions and 13 deletions
|
|
@ -377,11 +377,21 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
|||
// Track stop reason
|
||||
let stopReason = "stop";
|
||||
|
||||
// Slack message limit is 40,000 characters
|
||||
// Slack message limit is 40,000 characters - split into multiple messages if needed
|
||||
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)_";
|
||||
const splitForSlack = (text: string): string[] => {
|
||||
if (text.length <= SLACK_MAX_LENGTH) return [text];
|
||||
const parts: string[] = [];
|
||||
let remaining = text;
|
||||
let partNum = 1;
|
||||
while (remaining.length > 0) {
|
||||
const chunk = remaining.substring(0, SLACK_MAX_LENGTH - 50);
|
||||
remaining = remaining.substring(SLACK_MAX_LENGTH - 50);
|
||||
const suffix = remaining.length > 0 ? `\n_(continued ${partNum}...)_` : "";
|
||||
parts.push(chunk + suffix);
|
||||
partNum++;
|
||||
}
|
||||
return parts;
|
||||
};
|
||||
|
||||
// Promise queue to ensure ctx.respond/respondInThread calls execute in order
|
||||
|
|
@ -404,6 +414,13 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
|||
}
|
||||
});
|
||||
},
|
||||
// Enqueue a message that may need splitting
|
||||
enqueueMessage(text: string, target: "main" | "thread", errorContext: string): void {
|
||||
const parts = splitForSlack(text);
|
||||
for (const part of parts) {
|
||||
this.enqueue(() => (target === "main" ? ctx.respond(part) : ctx.respondInThread(part)), errorContext);
|
||||
}
|
||||
},
|
||||
flush(): Promise<void> {
|
||||
return this.chain;
|
||||
},
|
||||
|
|
@ -471,8 +488,6 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
|||
? formatToolArgsForSlack(event.toolName, pending.args as Record<string, unknown>)
|
||||
: "(args not found)";
|
||||
const duration = (durationMs / 1000).toFixed(1);
|
||||
const threadResult = truncate(resultStr, 2000);
|
||||
|
||||
let threadMessage = `*${event.isError ? "✗" : "✓"} ${event.toolName}*`;
|
||||
if (label) {
|
||||
threadMessage += `: ${label}`;
|
||||
|
|
@ -483,9 +498,9 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
|||
threadMessage += "```\n" + argsFormatted + "\n```\n";
|
||||
}
|
||||
|
||||
threadMessage += "*Result:*\n```\n" + threadResult + "\n```";
|
||||
threadMessage += "*Result:*\n```\n" + resultStr + "\n```";
|
||||
|
||||
queue.enqueue(() => ctx.respondInThread(truncateForSlack(threadMessage)), "tool result thread");
|
||||
queue.enqueueMessage(threadMessage, "thread", "tool result thread");
|
||||
|
||||
// Show brief error in main message if failed
|
||||
if (event.isError) {
|
||||
|
|
@ -544,15 +559,15 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
|||
// Post thinking to main message and thread
|
||||
for (const thinking of thinkingParts) {
|
||||
log.logThinking(logCtx, thinking);
|
||||
queue.enqueue(() => ctx.respond(truncateForSlack(`_${thinking}_`)), "thinking main");
|
||||
queue.enqueue(() => ctx.respondInThread(truncateForSlack(`_${thinking}_`)), "thinking thread");
|
||||
queue.enqueueMessage(`_${thinking}_`, "main", "thinking main");
|
||||
queue.enqueueMessage(`_${thinking}_`, "thread", "thinking thread");
|
||||
}
|
||||
|
||||
// Post text to main message and thread
|
||||
if (text.trim()) {
|
||||
log.logResponse(logCtx, text);
|
||||
queue.enqueue(() => ctx.respond(truncateForSlack(text)), "response main");
|
||||
queue.enqueue(() => ctx.respondInThread(truncateForSlack(text)), "response thread");
|
||||
queue.enqueueMessage(text, "main", "response main");
|
||||
queue.enqueueMessage(text, "thread", "response thread");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -583,7 +598,12 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
|||
.join("\n") || "";
|
||||
if (finalText.trim()) {
|
||||
try {
|
||||
await ctx.replaceMessage(truncateForSlack(finalText));
|
||||
// For the main message, truncate if too long (full text is in thread)
|
||||
const mainText =
|
||||
finalText.length > SLACK_MAX_LENGTH
|
||||
? finalText.substring(0, SLACK_MAX_LENGTH - 50) + "\n\n_(see thread for full response)_"
|
||||
: finalText;
|
||||
await ctx.replaceMessage(mainText);
|
||||
} catch (err) {
|
||||
const errMsg = err instanceof Error ? err.message : String(err);
|
||||
log.logWarning("Failed to replace message with final text", errMsg);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue