Reimport archived sessions into the current workspace (#2123)

* fix(sessions): reimport archived sessions in their workspace

Archived agent records were treated as active imports, hiding their provider sessions permanently. Workspace-originated imports also discarded their workspace identity and created a duplicate workspace.

* fix(sessions): validate archived session restores

Restore archived imports under their existing Paseo agent identity, reject stale workspace ownership, and gate workspace targeting when the host cannot honor it.

* fix(sessions): roll back failed archived imports

If provider resume or history hydration fails, close any partial runtime, re-archive the provider session, and restore the original stored agent record.

* fix(sessions): validate restored import ownership

* fix(sessions): serialize concurrent restores

* fix(sessions): harden archived import recovery

* fix(sessions): validate archived import placement

* refactor(sessions): centralize provider session imports

Keep workspace placement rollback and stored-agent activation behind their existing owners so Session remains the wire boundary.
This commit is contained in:
Mohamed Boudra
2026-07-17 02:25:50 +08:00
committed by GitHub
parent 7d80fdfd12
commit a1cd50c2ae
18 changed files with 1013 additions and 154 deletions
@@ -3692,6 +3692,88 @@ test("import_agent_request registers a workspace for a never-seen cwd", async ()
).toBe(true);
});
test("import_agent_request imports into the workspace that opened the import sheet", async () => {
const session = createSessionForWorkspaceTests();
const workspaceId = "ws-repo-running";
let importedWorkspaceId: string | undefined;
let workspaceCreated = false;
session.projectRegistry.get = async () =>
createPersistedProjectRecord({
projectId: "proj-repo-running",
rootPath: REPO_CWD,
kind: "non_git",
displayName: "repo",
createdAt: "2026-03-01T12:00:00.000Z",
updatedAt: "2026-03-01T12:00:00.000Z",
});
session.workspaceRegistry.upsert = async () => {
workspaceCreated = true;
};
session.agentManager.importProviderSession = async (input: unknown) => {
importedWorkspaceId = (input as { workspaceId: string }).workspaceId;
return makeManagedAgent({
id: "imported-agent",
cwd: REPO_CWD,
workspaceId: importedWorkspaceId,
lifecycle: "idle",
updatedAt: "2026-05-21T00:00:00.000Z",
});
};
session.agentManager.getTimeline = () => [];
session.agentStorage.list = async () => [];
session.agentStorage.get = async () => null;
session.agentUpdates.forwardLiveAgent = async () => undefined;
await session.handleMessage({
type: "import_agent_request",
requestId: "req-import-current-workspace",
providerId: "codex",
providerHandleId: "session-xyz",
cwd: REPO_CWD,
workspaceId,
});
expect(importedWorkspaceId).toBe(workspaceId);
expect(workspaceCreated).toBe(false);
});
test("import_agent_request maps an import failure to agent_create_failed", async () => {
const emitted: SessionOutboundMessage[] = [];
const session = createSessionForWorkspaceTests({
onMessage: (message) => emitted.push(message),
});
session.projectRegistry.get = async () =>
createPersistedProjectRecord({
projectId: "proj-repo-running",
rootPath: REPO_CWD,
kind: "non_git",
displayName: "repo",
createdAt: "2026-03-01T12:00:00.000Z",
updatedAt: "2026-03-01T12:00:00.000Z",
});
session.agentStorage.list = async () => [];
session.agentManager.importProviderSession = async () => {
throw new Error("provider session is unavailable");
};
await session.handleMessage({
type: "import_agent_request",
requestId: "req-failed-import",
providerId: "codex",
providerHandleId: "stale-session",
cwd: REPO_CWD,
workspaceId: "ws-repo-running",
});
expect(findByType(emitted, "status")?.payload).toMatchObject({
status: "agent_create_failed",
requestId: "req-failed-import",
error: "provider session is unavailable",
});
});
test("open_project_response returns immediately even when the GitHub fetch is slow", async () => {
const emitted: SessionOutboundMessage[] = [];
const session = createSessionForWorkspaceTests();