From fc76fb2ada8277897433b2f156e0217daa696363 Mon Sep 17 00:00:00 2001 From: creatixchu Date: Mon, 19 Oct 2026 19:12:37 +0800 Subject: [PATCH] test(cua): verify durable consent across complete provider restarts --- .../tests/provider-lifecycle.spec.ts | 5 ++- .../cua/tests/application-access.spec.ts | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/packages/experimental/cua-macos/tests/provider-lifecycle.spec.ts b/packages/experimental/cua-macos/tests/provider-lifecycle.spec.ts index 950cd98d8f..8f3ffce7d8 100644 --- a/packages/experimental/cua-macos/tests/provider-lifecycle.spec.ts +++ b/packages/experimental/cua-macos/tests/provider-lifecycle.spec.ts @@ -708,9 +708,10 @@ it('stores revocation without entering the HMR configuration queue', async () => await blocked const entry = [...f.ctx.loader.entries()].find(entry => entry.options.id === 'cua')! const stopping = entry.fiber! - await entry.update({ disabled: true }) + const options = { ...entry.options } + entry.parent.remove(entry.options.id) await stopping.dispose() - await entry.update({ disabled: false }) + await entry.parent.create({ ...options, disabled: false }) await f.ctx.loader.await() expect((await f.ctx.cuaAccess.applications()).allowedApps).toEqual([]) }) diff --git a/packages/experimental/cua/tests/application-access.spec.ts b/packages/experimental/cua/tests/application-access.spec.ts index ed78a0cbfe..8bbb54c2f4 100644 --- a/packages/experimental/cua/tests/application-access.spec.ts +++ b/packages/experimental/cua/tests/application-access.spec.ts @@ -1,5 +1,11 @@ /** Application decisions are checked at dispatch and revocation cancels owned work. */ import { Context } from '@deepseek-ai/cordis' +import { mkdir, mkdtemp, rm } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import Storage from '@deepseek-ai/dsh-storage' +import * as JsonStorage from '@deepseek-ai/dsh-storage-json' +import * as Domains from '@deepseek-ai/dsh-storage-domain' import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness } from '@deepseek-ai/dsh-agent-loop-testkit' import { SessionId } from '@deepseek-ai/dsh-session' import UserQuestions, { UserQuestionError, type AskUserQuestionAnswer } from '@deepseek-ai/dsh-user-questions' @@ -9,6 +15,7 @@ import type { CuaApplicationGrant } from '../src/application-types.ts' import type { CuaAppHandle } from '../src/types.ts' import { NativeClient } from '../src/native-client.ts' import { permissionFixture } from './permission-fixture.ts' +import { CuaPermissionStore } from '../src/permission-store.ts' const native = vi.hoisted(() => ({ instances: [] as Array<{ disposed: boolean @@ -359,3 +366,37 @@ it('retains conversation consent when the provider is recreated over the same st await access.select(f.agent, client, handle, identity, signal) expect(f.answer).toHaveBeenCalledOnce() }) + +it('restores consent after a complete context restart only for the original conversation', async () => { + const root = await mkdtemp(join(tmpdir(), 'cua-conversation-consent-')) + const directory = join(root, 'profile') + await mkdir(directory) + const contexts: Context[] = [] + onTestFinished(async () => { + for (const ctx of contexts) await ctx.fiber.dispose() + await rm(root, { recursive: true, force: true }) + }) + const open = async () => { + const f = await setup() + contexts.push(f.ctx) + f.ctx.provide('profileContext', { dir: directory }) + await f.ctx.plugin(Storage) + await f.ctx.plugin(JsonStorage, { root: join(root, 'storages') }) + await f.ctx.plugin(Domains, { backend: 'json' }) + const access = new ApplicationAccess(f.ctx, { store: new CuaPermissionStore(f.ctx, [], 1000) }) + return { ...f, access } + } + const first = await open() + await first.access.select(first.agent, first.client, handle, identity, signal) + expect(first.answer).toHaveBeenCalledOnce() + await first.ctx.fiber.dispose() + const restored = await open() + await restored.access.select(restored.agent, restored.client, handle, identity, signal) + expect(restored.answer).not.toHaveBeenCalled() + await expect(restored.access.run(restored.client, handle, signal, async () => 'read')).resolves.toBe('read') + const other = await restored.ctx.agents.create({ sessionId: SessionId('another-conversation') }) + const client = new NativeClient(restored.ctx, nativeConfig, unusedConnector) + await restored.access.select(other.agent, client, handle, identity, signal) + expect(restored.answer).toHaveBeenCalledOnce() + expect((await restored.access.status()).allowedApps).toEqual([]) +})