mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 17:45:53 +01:00
Introduce company-scoped task, agent, user and project files, recoverable trash, object-backed repository checkpoints, and a shared 180-second lifecycle for sandbox execution. Wire file browsing and save status into the owner surfaces. This is an implementation checkpoint; staging acceptance and the complete merge gate remain outstanding.
39 lines
1.3 KiB
TypeScript
39 lines
1.3 KiB
TypeScript
import { WORK_FOLDER_SYNC_INTERVAL_MS } from "@paperclipai/shared";
|
|
|
|
/** One scheduler for both execution generations. A tick never queues a backlog. */
|
|
export function startWorkFolderCheckpointer(input: {
|
|
checkpoint(): Promise<void>;
|
|
onError(error: unknown): Promise<void>;
|
|
}) {
|
|
let active: Promise<void> | null = null;
|
|
let stopped = false;
|
|
function checkpoint() {
|
|
const pending = (async () => {
|
|
try { await input.checkpoint(); }
|
|
catch (error) { await input.onError(error); throw error; }
|
|
})();
|
|
active = pending;
|
|
void pending.finally(() => { if (active === pending) active = null; }).catch(() => {});
|
|
return pending;
|
|
}
|
|
const timer = setInterval(() => {
|
|
if (!stopped && !active) void checkpoint().catch(() => {});
|
|
}, WORK_FOLDER_SYNC_INTERVAL_MS);
|
|
timer.unref();
|
|
// Explicit flushes serialize too. They must checkpoint once more after an
|
|
// in-flight tick, since the agent may have edited during that tick.
|
|
let flushTail: Promise<void> = Promise.resolve();
|
|
function flush() {
|
|
const pending = flushTail.catch(() => {}).then(async () => {
|
|
await active?.catch(() => {});
|
|
await checkpoint();
|
|
});
|
|
flushTail = pending;
|
|
return pending;
|
|
}
|
|
return {
|
|
flush,
|
|
async stop() { stopped = true; clearInterval(timer); await flush(); },
|
|
};
|
|
}
|