mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:10:42 +00:00
fix(sessions): hydrate agent-command skill snapshots
This commit is contained in:
@@ -37,6 +37,12 @@ const state = vi.hoisted(() => ({
|
||||
isThinkingLevelSupportedMock: vi.fn((_args: unknown) => true),
|
||||
resolveThinkingDefaultMock: vi.fn((_args: unknown) => "low"),
|
||||
loadManifestModelCatalogMock: vi.fn(() => []),
|
||||
buildWorkspaceSkillSnapshotMock: vi.fn((..._args: unknown[]): unknown => ({
|
||||
prompt: "",
|
||||
skills: [],
|
||||
resolvedSkills: [],
|
||||
version: 0,
|
||||
})),
|
||||
authProfileStoreMock: { profiles: {} } as { profiles: Record<string, unknown> },
|
||||
sessionEntryMock: undefined as unknown,
|
||||
sessionStoreMock: undefined as unknown,
|
||||
@@ -415,7 +421,8 @@ vi.mock("./provider-auth-aliases.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("./skills.js", () => ({
|
||||
buildWorkspaceSkillSnapshot: () => ({}),
|
||||
buildWorkspaceSkillSnapshot: (workspaceDir: string, opts: unknown) =>
|
||||
state.buildWorkspaceSkillSnapshotMock(workspaceDir, opts),
|
||||
}));
|
||||
|
||||
vi.mock("./skills/filter.js", () => ({
|
||||
@@ -565,6 +572,12 @@ describe("agentCommand – LiveSessionModelSwitchError retry", () => {
|
||||
state.authProfileStoreMock = { profiles: {} };
|
||||
state.sessionEntryMock = undefined;
|
||||
state.sessionStoreMock = undefined;
|
||||
state.buildWorkspaceSkillSnapshotMock.mockReturnValue({
|
||||
prompt: "",
|
||||
skills: [],
|
||||
resolvedSkills: [],
|
||||
version: 0,
|
||||
});
|
||||
state.deliverAgentCommandResultMock.mockResolvedValue(undefined);
|
||||
state.updateSessionStoreAfterAgentRunMock.mockResolvedValue(undefined);
|
||||
state.trajectoryFlushMock.mockResolvedValue(undefined);
|
||||
@@ -844,6 +857,59 @@ describe("agentCommand – LiveSessionModelSwitchError retry", () => {
|
||||
expect(state.clearSessionAuthProfileOverrideMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("hydrates stripped persisted skill snapshots before running the CLI path", async () => {
|
||||
const persistedSnapshot = {
|
||||
prompt: "persisted prompt",
|
||||
skills: [{ name: "cli-skill" }],
|
||||
skillFilter: ["cli-skill"],
|
||||
version: 0,
|
||||
};
|
||||
const rebuiltSkills = [
|
||||
{
|
||||
name: "cli-skill",
|
||||
description: "CLI skill",
|
||||
filePath: "/tmp/workspace/skills/cli-skill/SKILL.md",
|
||||
baseDir: "/tmp/workspace/skills/cli-skill",
|
||||
source: "# CLI skill",
|
||||
},
|
||||
];
|
||||
state.sessionEntryMock = {
|
||||
sessionId: "session-1",
|
||||
updatedAt: Date.now(),
|
||||
skillsSnapshot: persistedSnapshot,
|
||||
};
|
||||
state.buildWorkspaceSkillSnapshotMock.mockReturnValue({
|
||||
prompt: "rebuilt prompt",
|
||||
skills: [{ name: "different-skill" }],
|
||||
resolvedSkills: rebuiltSkills,
|
||||
version: 99,
|
||||
});
|
||||
state.runWithModelFallbackMock.mockImplementation(async (params: FallbackRunnerParams) => {
|
||||
const result = await params.run(params.provider, params.model);
|
||||
return {
|
||||
result,
|
||||
provider: params.provider,
|
||||
model: params.model,
|
||||
attempts: [],
|
||||
};
|
||||
});
|
||||
state.runAgentAttemptMock.mockResolvedValue(makeSuccessResult("anthropic", "claude"));
|
||||
|
||||
await runBasicAgentCommand();
|
||||
|
||||
const attemptParams = state.runAgentAttemptMock.mock.calls[0]?.[0] as
|
||||
| { skillsSnapshot?: Record<string, unknown> }
|
||||
| undefined;
|
||||
expect(attemptParams?.skillsSnapshot).toMatchObject({
|
||||
prompt: "persisted prompt",
|
||||
skills: [{ name: "cli-skill" }],
|
||||
skillFilter: ["cli-skill"],
|
||||
version: 0,
|
||||
resolvedSkills: rebuiltSkills,
|
||||
});
|
||||
expect(state.buildWorkspaceSkillSnapshotMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("classifies empty embedded run results before model fallback accepts them", async () => {
|
||||
let observedClassification: unknown;
|
||||
state.runWithModelFallbackMock.mockImplementation(async (params: FallbackRunnerParams) => {
|
||||
|
||||
@@ -632,35 +632,43 @@ async function agentCommandInternal(
|
||||
shouldRefreshSnapshotForVersion(currentSkillsSnapshot.version, skillsSnapshotVersion) ||
|
||||
!matchesSkillFilter(currentSkillsSnapshot.skillFilter, skillFilter);
|
||||
const needsSkillsSnapshot = isNewSession || shouldRefreshSkillsSnapshot;
|
||||
const buildSkillsSnapshot = async () => {
|
||||
const [
|
||||
{ buildWorkspaceSkillSnapshot },
|
||||
{ getRemoteSkillEligibility },
|
||||
{ canExecRequestNode },
|
||||
] = await Promise.all([
|
||||
loadSkillsRuntime(),
|
||||
loadSkillsRemoteRuntime(),
|
||||
loadExecDefaultsRuntime(),
|
||||
]);
|
||||
return buildWorkspaceSkillSnapshot(workspaceDir, {
|
||||
config: cfg,
|
||||
eligibility: {
|
||||
remote: getRemoteSkillEligibility({
|
||||
advertiseExecNode: canExecRequestNode({
|
||||
cfg,
|
||||
sessionEntry,
|
||||
sessionKey,
|
||||
agentId: sessionAgentId,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
snapshotVersion: skillsSnapshotVersion,
|
||||
skillFilter,
|
||||
agentId: sessionAgentId,
|
||||
});
|
||||
};
|
||||
const skillsSnapshot = needsSkillsSnapshot
|
||||
? await (async () => {
|
||||
const [
|
||||
{ buildWorkspaceSkillSnapshot },
|
||||
{ getRemoteSkillEligibility },
|
||||
{ canExecRequestNode },
|
||||
] = await Promise.all([
|
||||
loadSkillsRuntime(),
|
||||
loadSkillsRemoteRuntime(),
|
||||
loadExecDefaultsRuntime(),
|
||||
]);
|
||||
return buildWorkspaceSkillSnapshot(workspaceDir, {
|
||||
config: cfg,
|
||||
eligibility: {
|
||||
remote: getRemoteSkillEligibility({
|
||||
advertiseExecNode: canExecRequestNode({
|
||||
cfg,
|
||||
sessionEntry,
|
||||
sessionKey,
|
||||
agentId: sessionAgentId,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
snapshotVersion: skillsSnapshotVersion,
|
||||
skillFilter,
|
||||
agentId: sessionAgentId,
|
||||
});
|
||||
})()
|
||||
: currentSkillsSnapshot;
|
||||
? await buildSkillsSnapshot()
|
||||
: !currentSkillsSnapshot
|
||||
? undefined
|
||||
: currentSkillsSnapshot.resolvedSkills === undefined
|
||||
? {
|
||||
...currentSkillsSnapshot,
|
||||
resolvedSkills: (await buildSkillsSnapshot()).resolvedSkills,
|
||||
}
|
||||
: currentSkillsSnapshot;
|
||||
|
||||
if (skillsSnapshot && sessionStore && sessionKey && needsSkillsSnapshot) {
|
||||
const now = Date.now();
|
||||
|
||||
Reference in New Issue
Block a user