mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 17:45:53 +01:00
Keep warm sessions across project revision transitions (#17461)
This commit is contained in:
1 parent
14e1f59fbf
commit
60ca3a903a
3 files changed
+57
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user