fix: harden opencode cleanup and ignore local sisyphus state

This commit is contained in:
Harsh 2026-03-06 13:57:23 +05:30
parent 271f4d49b1
commit ed4d10c597
3 changed files with 38 additions and 3 deletions

1
.gitignore vendored
View File

@ -53,6 +53,7 @@ id_ed25519
# Development symlinks (created per-worktree, not committed)
.claude
.sisyphus
packages/web/agent-orchestrator.yaml
# Local agent orchestrator config (may contain secrets)

View File

@ -1639,6 +1639,31 @@ describe("cleanup", () => {
expect(deleteLog).toContain("session delete ses_archived");
});
it("skips invalid archived OpenCode session ids during cleanup", async () => {
const deleteLogPath = join(tmpDir, "opencode-delete-archived-invalid.log");
const mockBin = installMockOpencode("[]", deleteLogPath);
process.env.PATH = `${mockBin}:${originalPath ?? ""}`;
writeMetadata(sessionsDir, "app-8", {
worktree: "/tmp",
branch: "main",
status: "spawning",
project: "my-app",
agent: "opencode",
opencodeSessionId: "ses bad id",
runtimeHandle: JSON.stringify(makeHandle("rt-8")),
});
deleteMetadata(sessionsDir, "app-8", true);
const sm = createSessionManager({ config, registry: mockRegistry });
const result = await sm.cleanup();
expect(result.killed).not.toContain("app-8");
expect(result.errors).toEqual([]);
expect(result.skipped).toContain("app-8");
expect(existsSync(deleteLogPath)).toBe(false);
});
it("does not delete archived OpenCode sessions in cleanup dry-run", async () => {
const deleteLogPath = join(tmpDir, "opencode-delete-archived-dry-run.log");
const mockBin = installMockOpencode("[]", deleteLogPath);

View File

@ -1080,7 +1080,10 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
const plugins = resolvePlugins(project, selectedAgentName);
// Cap per-session enrichment at 2s — subprocess calls (tmux/ps) can be
// slow under load. If we time out, session keeps its metadata values.
const enrichTimeout = new Promise<void>((resolve) => setTimeout(resolve, 2_000));
let enrichTimeoutId: ReturnType<typeof setTimeout> | null = null;
const enrichTimeout = new Promise<void>((resolve) => {
enrichTimeoutId = setTimeout(resolve, 2_000);
});
const enrichPromise = ensureHandleAndEnrich(
session,
sessionName,
@ -1089,7 +1092,13 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
selectedAgentName,
plugins,
).catch(() => {});
await Promise.race([enrichPromise, enrichTimeout]);
try {
await Promise.race([enrichPromise, enrichTimeout]);
} finally {
if (enrichTimeoutId) {
clearTimeout(enrichTimeoutId);
}
}
return session;
},
@ -1316,7 +1325,7 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
}
const cleanupAgent = archived["agent"] ?? project.agent ?? config.defaults.agent;
const mappedOpenCodeSessionId = archived["opencodeSessionId"];
const mappedOpenCodeSessionId = asValidOpenCodeSessionId(archived["opencodeSessionId"]);
if (cleanupAgent === "opencode" && archived["opencodeCleanedAt"]) {
pushUnique(result.skipped, archivedId);
continue;