Skip to content

Commit e461100

Browse files
committed
fix(agents): cap direct text fallback and fix lint in repro script
Address ClawSweeper review feedback on PR #92643: 1. [P2] Cap direct text completion delivery at 4000 chars with truncation note to avoid hitting channel limits and re-amplifying transcript writes in the locked-session fallback path. 2. [P1] Fix four no-unnecessary-boolean-literal-compare lint errors in the repro script. 3. Add regression test for long-output capping behavior. Ref: #92076
1 parent 05fb8b8 commit e461100

3 files changed

Lines changed: 84 additions & 6 deletions

File tree

scripts/repro-92076.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,22 @@ const staleError = new SessionWriteLockStaleError(
5252
const unrelatedError = new Error("unrelated error");
5353

5454
assert(
55-
isSessionWriteLockAcquireError(lockError) === true,
55+
isSessionWriteLockAcquireError(lockError),
5656
"SessionWriteLockTimeoutError detected as lock error"
5757
);
5858
assert(
59-
isSessionWriteLockAcquireError(staleError) === true,
59+
isSessionWriteLockAcquireError(staleError),
6060
"SessionWriteLockStaleError detected as lock error"
6161
);
6262
assert(
63-
isSessionWriteLockAcquireError(unrelatedError) === false,
63+
!isSessionWriteLockAcquireError(unrelatedError),
6464
"Unrelated error NOT detected as lock error"
6565
);
6666

6767
// 3. Requester session classification
6868
console.log("\n3. Session key classification:");
6969
assert(
70-
isInternalAnnounceRequesterSession(undefined) === false,
70+
!isInternalAnnounceRequesterSession(undefined),
7171
"undefined sessionKey → false"
7272
);
7373
assert(

src/agents/subagent-announce-delivery.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,6 +4089,73 @@ describe("deliverSubagentAnnouncement completion delivery", () => {
40894089
);
40904090
});
40914091

4092+
it("caps direct text completion delivery at 4000 characters", async () => {
4093+
const callGateway = createGatewayMock({
4094+
result: { payloads: [] },
4095+
});
4096+
const sendMessage = createSendMessageMock();
4097+
const queueEmbeddedAgentMessageWithOutcome = createQueueOutcomeSequenceMock([
4098+
"transcript_commit_wait_unsupported",
4099+
"no_active_run",
4100+
]);
4101+
const longResult = "x".repeat(5_000);
4102+
const origin = {
4103+
channel: "discord",
4104+
to: "dm:U123",
4105+
accountId: "acct-1",
4106+
};
4107+
testing.setDepsForTest({
4108+
callGateway,
4109+
getRequesterSessionActivity: () => ({
4110+
sessionId: "requester-session-dm-cap",
4111+
isActive: true,
4112+
}),
4113+
getRuntimeConfig: () => ({}) as never,
4114+
sendMessage,
4115+
queueEmbeddedAgentMessageWithOutcome,
4116+
});
4117+
const result = await deliverSubagentAnnouncement({
4118+
requesterSessionKey: "agent:main:discord:dm:U123",
4119+
targetRequesterSessionKey: "agent:main:discord:dm:U123",
4120+
triggerMessage: "child done",
4121+
steerMessage: "child done",
4122+
requesterOrigin: origin,
4123+
requesterSessionOrigin: origin,
4124+
completionDirectOrigin: origin,
4125+
directOrigin: origin,
4126+
requesterIsSubagent: false,
4127+
expectsCompletionMessage: true,
4128+
bestEffortDeliver: true,
4129+
directIdempotencyKey: "announce-dm-cap-text",
4130+
internalEvents: [
4131+
{
4132+
type: "task_completion",
4133+
source: "subagent",
4134+
childSessionKey: "agent:worker:subagent:child",
4135+
childSessionId: "child-session-id",
4136+
announceType: "subagent task",
4137+
taskLabel: "dm cap test",
4138+
status: "ok",
4139+
statusLabel: "completed successfully",
4140+
result: longResult,
4141+
replyInstruction: "Summarize the result.",
4142+
},
4143+
],
4144+
});
4145+
4146+
expectRecordFields(result, {
4147+
delivered: true,
4148+
path: "direct",
4149+
});
4150+
expect(sendMessage).toHaveBeenCalledTimes(1);
4151+
const sentContent = (sendMessage as ReturnType<typeof vi.fn>).mock.calls[0][0]
4152+
.content as string;
4153+
expect(sentContent.length).toBeLessThan(longResult.length);
4154+
expect(sentContent).toContain("…(truncated");
4155+
// First 4000 chars preserved
4156+
expect(sentContent.startsWith("x".repeat(4_000))).toBe(true);
4157+
});
4158+
40924159
it("directly delivers stale isolated cron run media completions", async () => {
40934160
const callGateway = createGatewayMock();
40944161
const sendMessage = createSendMessageMock();

src/agents/subagent-announce-delivery.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,16 @@ function resolveTextCompletionDirectFallback(events: readonly AgentInternalEvent
911911
return undefined;
912912
}
913913

914+
/** Maximum character length for direct text completion delivery. */
915+
const DIRECT_TEXT_COMPLETION_MAX_LENGTH = 4_000;
916+
917+
function capDirectTextContent(content: string): string {
918+
if (content.length <= DIRECT_TEXT_COMPLETION_MAX_LENGTH) {
919+
return content;
920+
}
921+
return `${content.slice(0, DIRECT_TEXT_COMPLETION_MAX_LENGTH)}\n\n…(truncated ${content.length - DIRECT_TEXT_COMPLETION_MAX_LENGTH} chars)`;
922+
}
923+
914924
function hasFailedSubagentNoOutputCompletion(events: readonly AgentInternalEvent[] | undefined) {
915925
return (
916926
events?.some(
@@ -936,16 +946,17 @@ async function deliverTextCompletionDirect(params: {
936946
};
937947
internalEvents?: readonly AgentInternalEvent[];
938948
}): Promise<SubagentAnnounceDeliveryResult | undefined> {
939-
const content = resolveTextCompletionDirectFallback(params.internalEvents);
949+
const rawContent = resolveTextCompletionDirectFallback(params.internalEvents);
940950
if (
941-
!content ||
951+
!rawContent ||
942952
!params.deliveryTarget.deliver ||
943953
!params.deliveryTarget.channel ||
944954
!params.deliveryTarget.to ||
945955
!isDirectMessageDeliveryTarget(params.deliveryTarget, params.requesterSessionKey)
946956
) {
947957
return undefined;
948958
}
959+
const content = capDirectTextContent(rawContent);
949960
const agentId = resolveAgentIdFromSessionKey(params.requesterSessionKey);
950961
const idempotencyKey = `${params.directIdempotencyKey}:text-direct`;
951962
try {

0 commit comments

Comments
 (0)