From 685a0504f510000a78e6cca49f2958baa54ea164 Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:17:18 +0800 Subject: [PATCH] fix(manager): allow session provider switches (#240) --- src/mgr/store.ts | 5 ++--- src/selftest/cases/60-hwlab-baseline-contract.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/mgr/store.ts b/src/mgr/store.ts index d60531d..f0585a2 100644 --- a/src/mgr/store.ts +++ b/src/mgr/store.ts @@ -685,9 +685,8 @@ export function assertSessionBoundary(existing: SessionRecord, input: CreateRunI if (existing.tenantId !== input.tenantId || existing.projectId !== input.projectId) { throw new AgentRunError("tenant-policy-denied", "sessionRef cannot be reused across tenant or project boundary", { httpStatus: 403, details: { sessionId: existing.sessionId, valuesPrinted: false } }); } - if (existing.backendProfile !== input.backendProfile) { - throw new AgentRunError("schema-invalid", "sessionRef cannot be reused across backendProfile boundary", { httpStatus: 400, details: { sessionId: existing.sessionId, existingBackendProfile: existing.backendProfile, requestedBackendProfile: input.backendProfile, valuesPrinted: false } }); - } + // backendProfile is run-scoped, not session/PVC-scoped. A HWLAB session must be + // able to switch providers without losing its session storage or workspace. } export function statusFromTerminal(terminalStatus: TerminalStatus): RunRecord["status"] { diff --git a/src/selftest/cases/60-hwlab-baseline-contract.ts b/src/selftest/cases/60-hwlab-baseline-contract.ts index 4f5c553..36d95f8 100644 --- a/src/selftest/cases/60-hwlab-baseline-contract.ts +++ b/src/selftest/cases/60-hwlab-baseline-contract.ts @@ -45,9 +45,9 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin await assertEventContractAndCompletedSemantics(client, context, server.baseUrl); await assertRunnerJobStatus(client, context); assertThreadResumeStandard(context); - await assertSessionProfileIsolation(client, context); + await assertSessionProfileSwitchAllowed(client, context); await assertResourceBundleFailure(client, context, server.baseUrl); - return { name: "hwlab-baseline-contract", tests: ["event-contract", "result-completed-terminal-only", "bounded-output-summary", "runner-job-status", "thread-resume-standard", "backend-preflight-redacted", "session-profile-isolation", "resource-bundle-failure-kind"] }; + return { name: "hwlab-baseline-contract", tests: ["event-contract", "result-completed-terminal-only", "bounded-output-summary", "runner-job-status", "thread-resume-standard", "backend-preflight-redacted", "session-profile-switch", "resource-bundle-failure-kind"] }; } finally { await new Promise((resolve) => server.server.close(() => resolve())); } @@ -166,13 +166,13 @@ function assertThreadResumeStandard(context: SelfTestContext): void { assert.equal(backendTurnOptions({ ...run, sessionRef: { sessionId: "selftest-thread-standard-session" } }, command).threadId, undefined); } -async function assertSessionProfileIsolation(client: ManagerClient, context: SelfTestContext): Promise { +async function assertSessionProfileSwitchAllowed(client: ManagerClient, context: SelfTestContext): Promise { const first = await client.post("/api/v1/runs", runPayload(context, "codex", "selftest-profile-boundary-session")) as { id: string }; await client.patch(`/api/v1/runs/${first.id}/status`, { terminalStatus: "completed", failureKind: null, failureMessage: null, threadId: "thread_codex_profile_boundary", turnId: "turn_profile_boundary" }); - await assert.rejects( - () => client.post("/api/v1/runs", runPayload(context, "deepseek", "selftest-profile-boundary-session")), - (error) => error instanceof Error && error.message.includes("backendProfile boundary"), - ); + const second = await client.post("/api/v1/runs", runPayload(context, "deepseek", "selftest-profile-boundary-session")) as { id: string; backendProfile?: string; sessionRef?: { sessionId?: string } }; + assert.ok(second.id); + assert.equal(second.backendProfile, "deepseek"); + assert.equal(second.sessionRef?.sessionId, "selftest-profile-boundary-session"); } async function assertResourceBundleFailure(client: ManagerClient, context: SelfTestContext, managerUrl: string): Promise {