Fix agent skills autosave hydration\n\nCo-Authored-By: Paperclip <noreply@paperclip.ing>

This commit is contained in:
Dotta 2026-03-16 17:46:07 -05:00
parent 8460fee380
commit bb46423969
3 changed files with 118 additions and 14 deletions

View file

@ -0,0 +1,58 @@
import { describe, expect, it } from "vitest";
import { applyAgentSkillSnapshot } from "./agent-skills-state";
describe("applyAgentSkillSnapshot", () => {
it("hydrates the initial snapshot without arming autosave", () => {
const result = applyAgentSkillSnapshot(
{
draft: [],
lastSaved: [],
hasHydratedSnapshot: false,
},
["paperclip", "para-memory-files"],
);
expect(result).toEqual({
draft: ["paperclip", "para-memory-files"],
lastSaved: ["paperclip", "para-memory-files"],
hasHydratedSnapshot: true,
shouldSkipAutosave: true,
});
});
it("keeps unsaved local edits when a fresh snapshot arrives", () => {
const result = applyAgentSkillSnapshot(
{
draft: ["paperclip", "custom-skill"],
lastSaved: ["paperclip"],
hasHydratedSnapshot: true,
},
["paperclip"],
);
expect(result).toEqual({
draft: ["paperclip", "custom-skill"],
lastSaved: ["paperclip"],
hasHydratedSnapshot: true,
shouldSkipAutosave: false,
});
});
it("adopts server state after a successful save and skips the follow-up autosave pass", () => {
const result = applyAgentSkillSnapshot(
{
draft: ["paperclip", "custom-skill"],
lastSaved: ["paperclip", "custom-skill"],
hasHydratedSnapshot: true,
},
["paperclip", "custom-skill"],
);
expect(result).toEqual({
draft: ["paperclip", "custom-skill"],
lastSaved: ["paperclip", "custom-skill"],
hasHydratedSnapshot: true,
shouldSkipAutosave: true,
});
});
});

View file

@ -0,0 +1,29 @@
export interface AgentSkillDraftState {
draft: string[];
lastSaved: string[];
hasHydratedSnapshot: boolean;
}
export interface AgentSkillSnapshotApplyResult extends AgentSkillDraftState {
shouldSkipAutosave: boolean;
}
export function arraysEqual(a: string[], b: string[]): boolean {
if (a === b) return true;
if (a.length !== b.length) return false;
return a.every((value, index) => value === b[index]);
}
export function applyAgentSkillSnapshot(
state: AgentSkillDraftState,
desiredSkills: string[],
): AgentSkillSnapshotApplyResult {
const shouldReplaceDraft = !state.hasHydratedSnapshot || arraysEqual(state.draft, state.lastSaved);
return {
draft: shouldReplaceDraft ? desiredSkills : state.draft,
lastSaved: desiredSkills,
hasHydratedSnapshot: true,
shouldSkipAutosave: shouldReplaceDraft,
};
}