fix(team): complete teammate permission responses

This commit is contained in:
777genius 2026-05-17 01:30:19 +03:00
parent 471fe0a1a8
commit be558927ea
4 changed files with 229 additions and 86 deletions

View file

@ -31999,15 +31999,12 @@ export class TeamProvisioningService {
}); });
}; };
// Apply permission_suggestions: add tool rules to project settings file // Apply permission_suggestions: add tool rules to project settings file.
if (suggestions.length === 0) { if (suggestions.length === 0) {
sendSuccessResponse();
logger.info( logger.info(
`[${run.teamName}] No permission_suggestions for ${requestId}; inbox response sent` `[${run.teamName}] No permission_suggestions for ${requestId}; sending allow responses only`
); );
return; } else {
}
// Resolve project cwd from team config // Resolve project cwd from team config
let projectCwd: string | undefined; let projectCwd: string | undefined;
try { try {
@ -32016,14 +32013,12 @@ export class TeamProvisioningService {
} catch { } catch {
// best-effort // best-effort
} }
if (!projectCwd) { if (!projectCwd) {
logger.warn( logger.warn(
`[${run.teamName}] Cannot resolve project cwd for permission rule; sending inbox response only` `[${run.teamName}] Cannot resolve project cwd for permission rule; sending allow responses only`
); );
sendSuccessResponse(); } else {
return;
}
for (const suggestion of suggestions) { for (const suggestion of suggestions) {
// Handle "setMode" suggestions (e.g. Write/Edit tools suggest acceptEdits mode) // Handle "setMode" suggestions (e.g. Write/Edit tools suggest acceptEdits mode)
// FACT: Write/Edit permission_requests have permission_suggestions: // FACT: Write/Edit permission_requests have permission_suggestions:
@ -32098,6 +32093,8 @@ export class TeamProvisioningService {
); );
} }
} }
}
}
sendSuccessResponse(); sendSuccessResponse();
@ -32186,7 +32183,7 @@ export class TeamProvisioningService {
message: string | undefined message: string | undefined
): Record<string, unknown> | undefined { ): Record<string, unknown> | undefined {
if (!toolInput) return undefined; if (!toolInput) return undefined;
if (toolName !== 'AskUserQuestion' || !message) return toolInput; if (toolName !== 'AskUserQuestion' || message === undefined) return toolInput;
const answers = this.parseAskUserQuestionAnswers(message, toolInput); const answers = this.parseAskUserQuestionAnswers(message, toolInput);
return Object.keys(answers).length > 0 ? { ...toolInput, answers } : toolInput; return Object.keys(answers).length > 0 ? { ...toolInput, answers } : toolInput;

View file

@ -147,6 +147,34 @@ describe('team model availability Codex catalog integration', () => {
availabilityStatus: 'available', availabilityStatus: 'available',
availabilityReason: null, availabilityReason: null,
}, },
{
value: 'gpt-5.3-codex-spark',
label: '5.3 Codex Spark',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
{
value: 'gpt-5.2-codex',
label: '5.2 Codex',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
{
value: 'gpt-5.1-codex-mini',
label: '5.1 Codex Mini',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
{
value: 'gpt-5.1-codex-max',
label: '5.1 Codex Max',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
]); ]);
}); });
@ -224,7 +252,16 @@ describe('team model availability Codex catalog integration', () => {
expect( expect(
getAvailableTeamProviderModelOptions('codex', providerStatus).map((model) => model.value) getAvailableTeamProviderModelOptions('codex', providerStatus).map((model) => model.value)
).toEqual(['', 'gpt-5.5', 'gpt-5.4', 'gpt-5.2']); ).toEqual([
'',
'gpt-5.5',
'gpt-5.4',
'gpt-5.3-codex-spark',
'gpt-5.2',
'gpt-5.2-codex',
'gpt-5.1-codex-mini',
'gpt-5.1-codex-max',
]);
}); });
it('keeps existing disabled model policy on top of the dynamic catalog', () => { it('keeps existing disabled model policy on top of the dynamic catalog', () => {

View file

@ -14454,6 +14454,26 @@ describe('TeamProvisioningService', () => {
}); });
}); });
it('preserves blank teammate AskUserQuestion answers', () => {
const svc = new TeamProvisioningService();
const toolInput = {
questions: [
{
question: 'Anything else?',
options: [{ label: 'Skip', description: 'No extra details' }],
},
],
};
expect((svc as any).buildTeammatePermissionUpdatedInput('AskUserQuestion', toolInput, ''))
.toEqual({
...toolInput,
answers: {
'Anything else?': '',
},
});
});
it('sends teammate AskUserQuestion permission responses to the teammate inbox', async () => { it('sends teammate AskUserQuestion permission responses to the teammate inbox', async () => {
const svc = new TeamProvisioningService(); const svc = new TeamProvisioningService();
const persistInboxMessage = vi.fn(); const persistInboxMessage = vi.fn();
@ -14570,6 +14590,45 @@ describe('TeamProvisioningService', () => {
}); });
}); });
it('sends teammate fallback control responses without permission suggestions', async () => {
const write = vi.fn((_line: string, cb?: (error?: Error | null) => void) => {
cb?.();
return true;
});
const svc = new TeamProvisioningService();
(svc as any).persistInboxMessage = vi.fn();
const toolInput = {
questions: [
{
question: 'Anything else?',
options: [{ label: 'Skip', description: 'No extra details' }],
},
],
};
await (svc as any).respondToTeammatePermission(
{
teamName: 'ops-team',
runId: 'run-1',
child: { stdin: { writable: true, write } },
},
'bob',
'perm-3',
true,
'',
[],
'AskUserQuestion',
toolInput
);
expect(write).toHaveBeenCalledTimes(1);
const payload = JSON.parse(write.mock.calls[0][0]);
expect(payload.response.response.updatedInput).toEqual({
...toolInput,
answers: { 'Anything else?': '' },
});
});
it('uses a non-alarming model delay message before 2 minutes of silence', () => { it('uses a non-alarming model delay message before 2 minutes of silence', () => {
const svc = new TeamProvisioningService(); const svc = new TeamProvisioningService();

View file

@ -111,18 +111,46 @@ describe('teamModelAvailability', () => {
); );
}); });
it('builds Codex model options from the runtime list instead of the hardcoded fallback', () => { it('builds Codex model options from the runtime list plus disabled safety entries', () => {
const providerStatus = createCodexProviderStatus(['gpt-5.4', 'gpt-5.3-codex']); const providerStatus = createCodexProviderStatus(['gpt-5.4', 'gpt-5.3-codex']);
expect(getAvailableTeamProviderModelOptions('codex', providerStatus)).toEqual([ expect(getAvailableTeamProviderModelOptions('codex', providerStatus)).toEqual([
{ value: '', label: 'Default', badgeLabel: 'Default' }, { value: '', label: 'Default', badgeLabel: 'Default' },
{ value: 'gpt-5.4', label: '5.4', availabilityStatus: 'available', availabilityReason: null }, {
value: 'gpt-5.4',
label: '5.4',
badgeLabel: undefined,
availabilityStatus: 'available',
availabilityReason: null,
},
{ {
value: 'gpt-5.3-codex', value: 'gpt-5.3-codex',
label: '5.3 Codex', label: '5.3 Codex',
badgeLabel: undefined,
availabilityStatus: 'available', availabilityStatus: 'available',
availabilityReason: null, availabilityReason: null,
}, },
{
value: 'gpt-5.3-codex-spark',
label: '5.3 Codex Spark',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
{
value: 'gpt-5.2-codex',
label: '5.2 Codex',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
{
value: 'gpt-5.1-codex-mini',
label: '5.1 Codex Mini',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
]); ]);
}); });
@ -148,9 +176,31 @@ describe('teamModelAvailability', () => {
{ {
value: 'gpt-5.4', value: 'gpt-5.4',
label: '5.4', label: '5.4',
badgeLabel: undefined,
availabilityStatus: 'unavailable', availabilityStatus: 'unavailable',
availabilityReason: 'No access for this account', availabilityReason: 'No access for this account',
}, },
{
value: 'gpt-5.3-codex-spark',
label: '5.3 Codex Spark',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
{
value: 'gpt-5.2-codex',
label: '5.2 Codex',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
{
value: 'gpt-5.1-codex-mini',
label: '5.1 Codex Mini',
badgeLabel: undefined,
availabilityStatus: null,
availabilityReason: null,
},
]); ]);
}); });