Keep warm sessions across project revision transitions (#17461)

This commit is contained in:
Nicky Leach authored and GitHub committed 2026-10-24 12:11:26 -07:00
1 parent 14e1f59fbf
commit 60ca3a903a
3 files changed
+57

No files matched your search

+5
View File
@@ -1776,6 +1776,11 @@ preparation. Both phases keep their original ordering in `heartbeat.ts`; they
acquire no runtime resources. Public-path characterization tests live in
`server/src/__tests__/heartbeat-session-preparation.test.ts`.
Session compatibility uses effective workspace policy and settings. Generic issue
and project row timestamps do not reset a session. This includes the first
project-owned directory binding. Changes to effective execution configuration
still require a fresh session.
Run-log formatting is in `server/src/services/heartbeat/run-log.ts`. It bounds
stored event payloads, redacts and shortens log chunks, and caps stdout/stderr
excerpts. `run-output.ts` owns per-attempt log handles, current-user redaction,
@@ -2427,6 +2427,57 @@ describe("effective run session config freshness", () => {
});
});
it.each([null, "2026-06-01T00:00:00.000Z"])("does not reset for project row timestamps (%s) but still resets for project policy", async (revision) => {
const base = await buildSessionConfigMetadata({
workspaceConfig: {
requestedMode: "agent_default",
effectiveMode: "agent_default",
projectConfigRevisionAt: revision,
projectPolicy: null,
},
});
const projectTouched = await buildSessionConfigMetadata({
workspaceConfig: {
requestedMode: "agent_default",
effectiveMode: "agent_default",
projectConfigRevisionAt: "2026-06-01T00:05:00.000Z",
projectPolicy: null,
},
});
const policyChanged = await buildSessionConfigMetadata({
workspaceConfig: {
requestedMode: "agent_default",
effectiveMode: "agent_default",
projectConfigRevisionAt: "2026-06-01T00:05:00.000Z",
projectPolicy: { enabled: true, defaultMode: "isolated_workspace" },
},
});
expect(
resolveTaskSessionConfigFreshness({
hasTaskSession: true,
configuredModel: "gpt-5.4-mini",
taskSessionParams: sessionParamsWithConfigMetadata(base),
configMetadata: projectTouched,
}),
).toMatchObject({
reset: false,
changedCategories: [],
reasons: [],
});
expect(
resolveTaskSessionConfigFreshness({
hasTaskSession: true,
configuredModel: "gpt-5.4-mini",
taskSessionParams: sessionParamsWithConfigMetadata(base),
configMetadata: policyChanged,
}),
).toMatchObject({
reset: true,
changedCategories: ["workspaceConfig"],
});
});
it("does not reset when a reusable execution workspace becomes realized", async () => {
const base = await buildSessionConfigMetadata({
workspaceConfig: {
@@ -2800,6 +2800,7 @@ function buildSessionConfigCategoryValues(input: {
// the timestamp here makes every comment invalidate an otherwise reusable
// task session.
delete workspaceConfig.issueConfigRevisionAt;
delete workspaceConfig.projectConfigRevisionAt;
// This row is runtime state, not requested configuration. It is absent
// before the first reusable run is realized and present on the next turn;
// fingerprinting that transition would rotate the native session exactly