mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 10:02:23 +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
|
// Track stop reason
|
||||||
let stopReason = "stop";
|
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 SLACK_MAX_LENGTH = 40000;
|
||||||
const truncateForSlack = (text: string): string => {
|
const splitForSlack = (text: string): string[] => {
|
||||||
if (text.length <= SLACK_MAX_LENGTH) return text;
|
if (text.length <= SLACK_MAX_LENGTH) return [text];
|
||||||
return text.substring(0, SLACK_MAX_LENGTH - 100) + "\n\n_(truncated - message too long)_";
|
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
|
// 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> {
|
flush(): Promise<void> {
|
||||||
return this.chain;
|
return this.chain;
|
||||||
},
|
},
|
||||||
|
|
@ -471,8 +488,6 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
||||||
? formatToolArgsForSlack(event.toolName, pending.args as Record<string, unknown>)
|
? formatToolArgsForSlack(event.toolName, pending.args as Record<string, unknown>)
|
||||||
: "(args not found)";
|
: "(args not found)";
|
||||||
const duration = (durationMs / 1000).toFixed(1);
|
const duration = (durationMs / 1000).toFixed(1);
|
||||||
const threadResult = truncate(resultStr, 2000);
|
|
||||||
|
|
||||||
let threadMessage = `*${event.isError ? "✗" : "✓"} ${event.toolName}*`;
|
let threadMessage = `*${event.isError ? "✗" : "✓"} ${event.toolName}*`;
|
||||||
if (label) {
|
if (label) {
|
||||||
threadMessage += `: ${label}`;
|
threadMessage += `: ${label}`;
|
||||||
|
|
@ -483,9 +498,9 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
||||||
threadMessage += "```\n" + argsFormatted + "\n```\n";
|
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
|
// Show brief error in main message if failed
|
||||||
if (event.isError) {
|
if (event.isError) {
|
||||||
|
|
@ -544,15 +559,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(truncateForSlack(`_${thinking}_`)), "thinking main");
|
queue.enqueueMessage(`_${thinking}_`, "main", "thinking main");
|
||||||
queue.enqueue(() => ctx.respondInThread(truncateForSlack(`_${thinking}_`)), "thinking thread");
|
queue.enqueueMessage(`_${thinking}_`, "thread", "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(truncateForSlack(text)), "response main");
|
queue.enqueueMessage(text, "main", "response main");
|
||||||
queue.enqueue(() => ctx.respondInThread(truncateForSlack(text)), "response thread");
|
queue.enqueueMessage(text, "thread", "response thread");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -583,7 +598,12 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner {
|
||||||
.join("\n") || "";
|
.join("\n") || "";
|
||||||
if (finalText.trim()) {
|
if (finalText.trim()) {
|
||||||
try {
|
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) {
|
} catch (err) {
|
||||||
const errMsg = err instanceof Error ? err.message : String(err);
|
const errMsg = err instanceof Error ? err.message : String(err);
|
||||||
log.logWarning("Failed to replace message with final text", errMsg);
|
log.logWarning("Failed to replace message with final text", errMsg);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue