Skip to content

Commit d840e53

Browse files
joshspicerCopilot
andcommitted
agentHost: cover the rejected-RPC path of the script-safety fail-closed guard
`session.options.update` reports `success: true` for any patch the runtime accepts and signals real problems by failing the request, so a rejected RPC — not a `success: false` body — is the path a genuine enablement failure takes. Both existing fail-closed tests drove the `success: false` branch, leaving the `catch` that logs the reason and aborts the launch uncovered. Adds a test that rejects the update and asserts the launch fails closed, the original error propagates, and the orphaned session is disconnected. Also drops the managed rules from the success test. They no longer gate the behavior, so passing a `deny` rule implied a dependency that is exactly the one the fail-closed change removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c0398af commit d840e53

1 file changed

Lines changed: 34 additions & 3 deletions

File tree

‎src/vs/platform/agentHost/test/node/copilotSessionLauncher.test.ts‎

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -884,9 +884,10 @@ suite('CopilotSessionLauncher GPT-5.6 customizations', () => {
884884
disconnect: async () => { },
885885
rpc: { options: { update: async (options: unknown) => { updates.push(options); return { success: true }; } } },
886886
} as unknown as CopilotSession;
887-
// Enablement is required for every session, so anything short of success would
888-
// fail the launch — the success path is what is asserted here.
889-
const launcher = createTestLauncher({ deny: ['Shell(rm -rf *)'] });
887+
// Enablement is required for every session regardless of managed rules, so
888+
// anything short of success would fail the launch — the success path is what
889+
// is asserted here.
890+
const launcher = createTestLauncher();
890891
const plan: CopilotSessionLaunchPlan = {
891892
kind: 'create',
892893
client: { createSession: async () => session } as unknown as CopilotClient,
@@ -966,6 +967,36 @@ suite('CopilotSessionLauncher GPT-5.6 customizations', () => {
966967
await launcher.disposeByokProxyHandle();
967968
});
968969

970+
// The runtime reports success for any patch it accepts and signals real problems by
971+
// failing the request, so a rejected RPC — not a `success: false` body — is the path
972+
// a genuine enablement failure takes.
973+
test('fails the launch closed and disconnects when the script safety request itself fails', async () => {
974+
let disconnected = false;
975+
const session = {
976+
sessionId: 'session-1',
977+
on: () => () => { },
978+
disconnect: async () => { disconnected = true; },
979+
rpc: { options: { update: async () => { throw new Error('connection closed'); } } },
980+
} as unknown as CopilotSession;
981+
const launcher = createTestLauncher();
982+
const plan: CopilotSessionLaunchPlan = {
983+
kind: 'create',
984+
client: { createSession: async () => session } as unknown as CopilotClient,
985+
sessionId: 'session-1',
986+
workingDirectory: testWorkingDirectory,
987+
resolvedAgentName: undefined,
988+
snapshot: { tools: [], plugins: [], mcpServers: {} },
989+
activeClientToolSet: new ActiveClientToolSet(),
990+
shellManager: undefined,
991+
githubToken: undefined,
992+
model: { id: 'claude-sonnet-4.5', config: {} },
993+
};
994+
995+
await assert.rejects(() => launcher.launch(plan, testRuntime), /connection closed/);
996+
assert.strictEqual(disconnected, true, 'expected the orphaned session to be disconnected');
997+
await launcher.disposeByokProxyHandle();
998+
});
999+
9691000
test('applies GPT-5.6 customizations when resuming an existing session', async () => {
9701001
const updates: unknown[] = [];
9711002
const session = {

0 commit comments

Comments
 (0)