Skip to content

Commit 4c682d1

Browse files
agentHost: Offer SDK download independent of sign-in
Check for a missing SDK before account, entitlement, and signed-out experiment gates so every user can consent to the required download. Keep missing-account guidance behind the existing signed-out checks and cover both sides of the decision boundary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2f343ff commit 4c682d1

2 files changed

Lines changed: 26 additions & 18 deletions

File tree

src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSdkSetupNotification.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { ILanguageModelsService } from '../../../common/languageModels.js';
2626

2727
/** Everything one agent's {@link AgentSdkSetupState} is decided from. */
2828
export interface IAgentSdkSetupStateInputs {
29-
/** The experimentation flag this whole feature stays behind. */
29+
/** The experimentation flag the missing-account routes stay behind. Not the download offer. */
3030
readonly allowSignedOutWhenUsable: boolean;
3131
/** Whether the user is signed in to GitHub (Copilot models already work). */
3232
readonly signedIn: boolean;
@@ -41,28 +41,29 @@ export interface IAgentSdkSetupStateInputs {
4141

4242
/**
4343
* The whole decision, as one pure function: what the banner renders and what the
44-
* funnel records are two readings of this one state. A signed-in user already
45-
* has Copilot models, so there is nothing to offer and BYOK stays undiscoverable
46-
* for them (a deliberate v1 cut).
44+
* funnel records are two readings of this one state.
45+
*
46+
* The download is offered before any other check, because it applies to
47+
* everyone: we fetch a large SDK onto the user's machine, and that is worth
48+
* saying whether or not they have models already.
4749
*/
4850
export function getAgentSdkSetupState(inputs: IAgentSdkSetupStateInputs): AgentSdkSetupState | undefined {
51+
if (inputs.download === 'notDownloaded') {
52+
// A request we sent covers the gap before the host answers it, so standing
53+
// consent (or a click) never flashes the offer it has already satisfied.
54+
return inputs.downloadRequested ? undefined : 'downloadOffered';
55+
}
56+
// Everything below explains a missing account, which is the signed-out
57+
// experiment and stays behind its flag.
4958
if (!inputs.allowSignedOutWhenUsable || !inputs.entitlementResolved || inputs.signedIn) {
5059
return undefined;
5160
}
52-
// Ahead of the download status because models are the honest end state: an
53-
// agent that can enumerate a catalog has an account, whatever a status claims.
5461
if (inputs.hasModels) {
5562
return 'resolved';
5663
}
57-
switch (inputs.download) {
58-
// A fetch in flight has nothing to ask for — the host drives its own
59-
// progress notification while it runs.
60-
case 'downloading': return undefined;
61-
// A request we sent covers the gap before the host answers it, so standing
62-
// consent (or a click) never flashes the offer it has already satisfied.
63-
case 'notDownloaded': return inputs.downloadRequested ? undefined : 'downloadOffered';
64-
case 'ready': return 'noAccount';
65-
}
64+
// A fetch in flight has nothing to ask for — the host drives its own
65+
// progress notification while it runs.
66+
return inputs.download === 'downloading' ? undefined : 'noAccount';
6667
}
6768

6869
/**

src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostSdkSetupNotification.test.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,17 @@ suite('Agent SDK setup banner', () => {
3939
{ name: 'a request the host has not answered yet is not a fresh offer', inputs: { ...BLOCKED_USER, downloadRequested: true }, expected: undefined },
4040
{ name: 'SDK on disk reporting no models means no account', inputs: { ...BLOCKED_USER, download: 'ready' }, expected: 'noAccount' },
4141
{ name: 'models are the honest end state, whatever the status says', inputs: { ...BLOCKED_USER, download: 'ready', hasModels: true }, expected: 'resolved' },
42-
{ name: 'a signed-in user already has Copilot models', inputs: { ...BLOCKED_USER, signedIn: true }, expected: undefined },
43-
{ name: 'nothing shows until entitlement settles, since "signed out" is not yet a fact', inputs: { ...BLOCKED_USER, entitlementResolved: false }, expected: undefined },
44-
{ name: 'the whole feature stays behind its flag', inputs: { ...BLOCKED_USER, allowSignedOutWhenUsable: false }, expected: undefined },
42+
{ name: 'nothing shows until entitlement settles, since "signed out" is not yet a fact', inputs: { ...BLOCKED_USER, download: 'ready', entitlementResolved: false }, expected: undefined },
43+
{ name: 'the missing-account explanation stays behind its flag', inputs: { ...BLOCKED_USER, download: 'ready', allowSignedOutWhenUsable: false }, expected: undefined },
44+
{ name: 'a signed-in user is never told they have no account', inputs: { ...BLOCKED_USER, download: 'ready', signedIn: true }, expected: undefined },
4545
{ name: 'a signed-in user mid-download is still shown nothing', inputs: { ...BLOCKED_USER, signedIn: true, download: 'downloading' }, expected: undefined },
46+
47+
// The download is offered to everyone: each of these users can work
48+
// today, and still has an SDK we are about to fetch onto their machine.
49+
{ name: 'a signed-in user is offered the download too', inputs: { ...BLOCKED_USER, signedIn: true }, expected: 'downloadOffered' },
50+
{ name: 'having models does not hide the download', inputs: { ...BLOCKED_USER, signedIn: true, hasModels: true }, expected: 'downloadOffered' },
51+
{ name: 'the download offer does not wait for entitlement', inputs: { ...BLOCKED_USER, entitlementResolved: false }, expected: 'downloadOffered' },
52+
{ name: 'the download offer is not behind the signed-out flag', inputs: { ...BLOCKED_USER, allowSignedOutWhenUsable: false }, expected: 'downloadOffered' },
4653
];
4754

4855
for (const { name, inputs, expected } of cases) {

0 commit comments

Comments
 (0)