mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-24 18:19:05 +02:00
fix(grok): retain provider results across collection and restore failures
Record collection failure separately on the completed result before workspace restoration. Keep the provider exit, session, usage, and output when both cleanup steps fail, while keeping raw collector errors out of logs. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
85654d4c5c
commit
b591a9520e
2 files changed
+56
-3
No files matched your search
@@ -868,12 +868,48 @@ describe("grok_local execute", () => {
|
||||
});
|
||||
const ctx = await makeCtx("run-collection-reject", await makeTempRoot());
|
||||
ctx.onProviderStopped = async () => { order.push("collect"); throw collectionError; };
|
||||
await expect(execute(ctx)).rejects.toBe(collectionError);
|
||||
const result = await execute(ctx);
|
||||
expect(result.errorCode).toBe("instruction_collection_failed");
|
||||
expect(result.resultJson?.instructionCollectionFailure).toBe("collection_failed");
|
||||
expect(result.resultJson?.workspaceRestoreFailure).toBeUndefined();
|
||||
expect(order).toEqual(["collect", "restore"]);
|
||||
expect(stagedDir).not.toBe("");
|
||||
expect(await pathExists(stagedDir)).toBe(false);
|
||||
});
|
||||
|
||||
it.each([0, 2])("preserves provider output when collection and restore both fail after exit %s", async (exitCode) => {
|
||||
delete process.env.XAI_API_KEY;
|
||||
mocks.state.isRemote = true;
|
||||
await seedHostGrokAuth("{}");
|
||||
runProcessMock.mockImplementation(async (_run, _target, _command, _args, options) => {
|
||||
options.onProcessStopped();
|
||||
return {
|
||||
...makeSuccessfulRunResult(), exitCode,
|
||||
stderr: exitCode ? "Model request failed." : "",
|
||||
stdout: [JSON.stringify({ type: "text", data: "Saved output." }), JSON.stringify({
|
||||
type: "end", sessionId: "sess-1", requestId: "req-1", stopReason: "EndTurn",
|
||||
usage: { input_tokens: 4, output_tokens: 9 },
|
||||
})].join("\n"),
|
||||
};
|
||||
});
|
||||
prepareRuntimeMock.mockImplementationOnce(async () => ({
|
||||
workspaceRemoteDir: "/remote/workspace",
|
||||
assetDirs: { home: "/remote/workspace/.paperclip-runtime/grok/home" },
|
||||
restoreWorkspace: async () => { throw new Error("restore failed"); },
|
||||
}));
|
||||
const ctx = await makeCtx("run-collection-and-restore-reject", await makeTempRoot());
|
||||
ctx.onProviderStopped = async () => { throw new Error("instruction collection failed"); };
|
||||
const result = await execute(ctx);
|
||||
expect(result).toMatchObject({
|
||||
errorCode: "workspace_restore_failed", exitCode, sessionId: "sess-1", summary: "Saved output.",
|
||||
usage: { inputTokens: 4, outputTokens: 9 },
|
||||
resultJson: { instructionCollectionFailure: "collection_failed", workspaceRestoreFailure: "restore_failed",
|
||||
requestId: "req-1", finalResponseRecorded: true, executionBeforeRestore: { exitCode } },
|
||||
});
|
||||
expect(result.errorMessage).toContain("Instruction collection failed");
|
||||
if (exitCode) expect(result.errorMessage).toContain("Model request failed.");
|
||||
});
|
||||
|
||||
it.each(["completed", "failed", "timed_out"])("preserves %s output and removes the staged home when restore fails", async (state) => {
|
||||
delete process.env.XAI_API_KEY;
|
||||
mocks.state.isRemote = true;
|
||||
|
||||
@@ -730,11 +730,28 @@ async function executeTurn(ctx: AdapterExecutionContext): Promise<AdapterExecuti
|
||||
try {
|
||||
return await withWorkspaceRestore(
|
||||
async () => {
|
||||
let result: AdapterExecutionResult;
|
||||
let collectionFailed = false;
|
||||
const collectionFailureMessage = "Instruction collection failed after provider stop. No instruction save is claimed.";
|
||||
try {
|
||||
return await executeTurn();
|
||||
result = await executeTurn();
|
||||
} finally {
|
||||
await providerStop.collectBeforeRestore();
|
||||
try {
|
||||
await providerStop.collectBeforeRestore();
|
||||
} catch {
|
||||
collectionFailed = true;
|
||||
await onLog("stderr", `[paperclip] ${collectionFailureMessage}\n`).catch(() => undefined);
|
||||
}
|
||||
}
|
||||
if (!collectionFailed) return result;
|
||||
const providerFailed = result.timedOut || result.signal || result.errorCode
|
||||
|| (result.exitCode !== null && result.exitCode !== 0);
|
||||
return {
|
||||
...result,
|
||||
...(!providerFailed ? { errorCode: "instruction_collection_failed" } : {}),
|
||||
errorMessage: [result.errorMessage, collectionFailureMessage].filter(Boolean).join(" "),
|
||||
resultJson: { ...result.resultJson, instructionCollectionFailure: "collection_failed" },
|
||||
};
|
||||
},
|
||||
async () => { await restoreRemoteWorkspace?.(); },
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user