Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions infra/relay/src/agentActivity/AgentActivityPublisher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ describe("makeAggregateState", () => {
...state,
threadId: "thread-done" as RelayAgentActivityState["threadId"],
phase: "completed",
updatedAt: "1970-01-01T00:50:00.000Z",
updatedAt: "1970-01-01T00:44:00.000Z",
};
const aggregate = AgentActivityPublisher.makeAggregateState({
activeStates: [active, staleCompleted],
Expand Down Expand Up @@ -769,7 +769,7 @@ describe("makeAggregateState", () => {
});
expect(
AgentActivityPublisher.makeAggregateState({
activeStates: [{ ...lingeringCompleted, updatedAt: "1970-01-01T00:50:00.000Z" }],
activeStates: [{ ...lingeringCompleted, updatedAt: "1970-01-01T00:44:00.000Z" }],
terminalState: null,
nowMs: hourMs,
}),
Expand All @@ -789,16 +789,25 @@ describe("makeAggregateState", () => {
updatedAt: "1970-01-01T00:59:00.000Z",
};
const aggregate = AgentActivityPublisher.makeAggregateState({
activeStates: [mkActive("a-1"), mkActive("a-2"), mkActive("a-3"), justCompleted],
activeStates: [
mkActive("a-1"),
mkActive("a-2"),
mkActive("a-3"),
mkActive("a-4"),
mkActive("a-5"),
justCompleted,
],
terminalState: null,
nowMs: hourMs,
});

expect(aggregate?.activeCount).toBe(3);
expect(aggregate?.activeCount).toBe(5);
expect(aggregate?.activities).toMatchObject([
{ threadId: "a-1" },
{ threadId: "a-2" },
{ threadId: "a-3" },
{ threadId: "a-4" },
{ threadId: "a-5" },
]);
});
});
10 changes: 7 additions & 3 deletions infra/relay/src/agentActivity/AgentActivityPublisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as Option from "effect/Option";
import {
isExpiredAgentActivityState,
isTerminalPhase,
MAX_ACTIVITY_ROWS,
sanitizeAgentActivityAggregateState,
} from "./agentActivityPayloads.ts";

Expand Down Expand Up @@ -228,7 +229,7 @@ function terminalAggregateState(state: RelayAgentActivityState): RelayAgentActiv
// How long a finished thread keeps its Done/Failed row in the aggregate while
// other agents are still active. Long enough to be seen on the lock screen,
// short enough that the activity list stays about live work.
export const TERMINAL_AGENT_ACTIVITY_DISPLAY_TTL_MS = 5 * 60 * 1_000;
export const TERMINAL_AGENT_ACTIVITY_DISPLAY_TTL_MS = 15 * 60 * 1_000;

function isRecentTerminalState(state: RelayAgentActivityState, nowMs: number): boolean {
if (!isTerminalPhase(state)) {
Expand Down Expand Up @@ -273,7 +274,7 @@ export function makeAggregateState(input: {
subtitle: newest.phase === "failed" ? "Agent work failed" : "Agent work completed",
activeCount: 0,
updatedAt: newest.updatedAt,
activities: recentTerminal.slice(0, 3).map(aggregateRowForState),
activities: recentTerminal.slice(0, MAX_ACTIVITY_ROWS).map(aggregateRowForState),
});
}
// Recently finished threads ride along after the active ones (display slots
Expand All @@ -282,7 +283,10 @@ export function makeAggregateState(input: {
const recentTerminalStates = input.activeStates
.filter((state) => isRecentTerminalState(state, input.nowMs))
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
const displayedStates = [...activeStates.slice(0, 3), ...recentTerminalStates].slice(0, 3);
const displayedStates = [
...activeStates.slice(0, MAX_ACTIVITY_ROWS),
...recentTerminalStates,
].slice(0, MAX_ACTIVITY_ROWS);
const updatedAt = [...activeStates, ...recentTerminalStates].reduce((latest, state) =>
state.updatedAt.localeCompare(latest.updatedAt) > 0 ? state : latest,
).updatedAt;
Expand Down
4 changes: 2 additions & 2 deletions infra/relay/src/agentActivity/ApnsDeliveries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ describe("ApnsDeliveries", () => {
...aggregate,
title: longTitle,
subtitle: longTitle,
activities: [0, 1, 2, 3].map((index) =>
activities: [0, 1, 2, 3, 4, 5].map((index) =>
Object.assign({}, aggregate.activities[0]!, {
projectTitle: longTitle,
threadTitle: longTitle,
Expand All @@ -396,7 +396,7 @@ describe("ApnsDeliveries", () => {
const payloadAggregate = queuedJobs[0]?.payload.aggregate;
expect(payloadAggregate?.title.length).toBeLessThanOrEqual(120);
expect(payloadAggregate?.subtitle.length).toBeLessThanOrEqual(120);
expect(payloadAggregate?.activities).toHaveLength(3);
expect(payloadAggregate?.activities).toHaveLength(5);
expect(payloadAggregate?.activities[0]?.projectTitle.length).toBeLessThanOrEqual(120);
expect(payloadAggregate?.activities[0]?.status.length).toBeLessThanOrEqual(40);
expect(payloadAggregate?.activities[0]?.deepLink).toBe("/");
Expand Down
4 changes: 3 additions & 1 deletion infra/relay/src/agentActivity/agentActivityPayloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export function isExpiredAgentActivityState(
const MAX_SUMMARY_TEXT_LENGTH = 120;
const MAX_STATUS_TEXT_LENGTH = 40;
const MAX_DEEP_LINK_LENGTH = 512;
const MAX_ACTIVITY_ROWS = 3;
// The Live Activity banner (lock screen / Notification Center) renders up to
// five rows; the expanded Dynamic Island shows the top three of these.
export const MAX_ACTIVITY_ROWS = 5;

function truncateText(value: string, maxLength: number): string {
const trimmed = value.trim();
Expand Down
Loading