diff --git a/src/agents/embedded-agent-runner/run/lane-controller.lifecycle.test.ts b/src/agents/embedded-agent-runner/run/lane-controller.lifecycle.test.ts index 8bc50e599f87..44abbba3eb54 100644 --- a/src/agents/embedded-agent-runner/run/lane-controller.lifecycle.test.ts +++ b/src/agents/embedded-agent-runner/run/lane-controller.lifecycle.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getAgentEventLifecycleGeneration, resetAgentEventsForTest, @@ -93,6 +93,10 @@ describe("createEmbeddedRunLaneController lifecycle admission", () => { resetAgentEventsForTest(); }); + afterEach(() => { + vi.restoreAllMocks(); + }); + it.each([ { trigger: "user" as const, expected: "foreground" }, { trigger: "cron" as const, expected: "background" }, @@ -149,6 +153,9 @@ describe("createEmbeddedRunLaneController lifecycle admission", () => { it("rebinds admitted attribution with foreground work across lifecycle rotation", async () => { const queue = deferredTaskQueue(); + const registeredAt = 1_000; + const reboundAt = 2_000; + const clock = vi.spyOn(Date, "now").mockReturnValue(registeredAt); const generation = getAgentEventLifecycleGeneration(); const attribution = createAgentExecutionAttribution({ runId: "attributed-across-restart", @@ -163,6 +170,7 @@ describe("createEmbeddedRunLaneController lifecycle admission", () => { ...(attribution.sessionId ? { sessionId: attribution.sessionId } : {}), ...(attribution.agentId ? { agentId: attribution.agentId } : {}), lifecycleGeneration: generation, + registeredAt, }); const state = createController({ lifecycleGeneration: generation, @@ -173,6 +181,7 @@ describe("createEmbeddedRunLaneController lifecycle admission", () => { const run = state.controller.enqueueGlobal(async () => completedResult); const currentGeneration = rotateAgentEventLifecycleGeneration(); + clock.mockReturnValue(reboundAt); queue.release(); await run; @@ -180,9 +189,13 @@ describe("createEmbeddedRunLaneController lifecycle admission", () => { ...attribution, lifecycleGeneration: currentGeneration, }); - expect(getAgentRunContext(attribution.runId)?.attribution).toEqual({ - ...attribution, + expect(getAgentRunContext(attribution.runId)).toMatchObject({ + attribution: { + ...attribution, + lifecycleGeneration: currentGeneration, + }, lifecycleGeneration: currentGeneration, + registeredAt: reboundAt, }); }); diff --git a/src/agents/embedded-agent-runner/run/lane-controller.ts b/src/agents/embedded-agent-runner/run/lane-controller.ts index e143d0465720..01083d034b6e 100644 --- a/src/agents/embedded-agent-runner/run/lane-controller.ts +++ b/src/agents/embedded-agent-runner/run/lane-controller.ts @@ -187,13 +187,20 @@ export function createEmbeddedRunLaneController(opti params = { ...params, attribution }; options.setParams(params); } + const admittedAt = Date.now(); claimAgentRunContext(params.runId, { ...existingContext, ...(attribution ? { attribution } : {}), sessionKey: params.sessionKey ?? existingContext?.sessionKey, sessionId: params.sessionId ?? existingContext?.sessionId, lifecycleGeneration, - lastActiveAt: Date.now(), + // A lifecycle rebound starts a new admission window; retaining the + // prior timestamp can make stale verbosity changes govern this run. + registeredAt: + existingContext?.lifecycleGeneration === lifecycleGeneration + ? existingContext.registeredAt + : admittedAt, + lastActiveAt: admittedAt, }); }, ),