fix(team): propagate managed runtime settings env
This commit is contained in:
parent
5403a2cea9
commit
77e08af03f
7 changed files with 51 additions and 0 deletions
|
|
@ -1252,6 +1252,7 @@ const STALL_CHECK_INTERVAL_MS = 10_000;
|
|||
const STALL_WARNING_THRESHOLD_MS = 20_000;
|
||||
const APP_TEAM_RUNTIME_DISALLOWED_TOOLS =
|
||||
'TeamDelete,TodoWrite,TaskCreate,TaskUpdate,mcp__agent-teams__team_launch,mcp__agent-teams__team_stop';
|
||||
const CLAUDE_TEAM_RUNTIME_SETTINGS_PATH_ENV = 'CLAUDE_TEAM_RUNTIME_SETTINGS_PATH';
|
||||
const TEAM_JSON_READ_TIMEOUT_MS = 5_000;
|
||||
const TEAM_CONFIG_MAX_BYTES = 10 * 1024 * 1024;
|
||||
const TEAM_INBOX_MAX_BYTES = 2 * 1024 * 1024;
|
||||
|
|
@ -2216,6 +2217,17 @@ function buildCodexCrossProviderSafeEnvPatch(env: NodeJS.ProcessEnv): NodeJS.Pro
|
|||
return envPatch;
|
||||
}
|
||||
|
||||
function applyAppManagedRuntimeSettingsPathEnv(
|
||||
env: NodeJS.ProcessEnv,
|
||||
settingsPath: string | null
|
||||
): void {
|
||||
if (settingsPath) {
|
||||
env[CLAUDE_TEAM_RUNTIME_SETTINGS_PATH_ENV] = settingsPath;
|
||||
} else {
|
||||
delete env[CLAUDE_TEAM_RUNTIME_SETTINGS_PATH_ENV];
|
||||
}
|
||||
}
|
||||
|
||||
interface TeamRuntimeAuthContext {
|
||||
teamName?: string;
|
||||
authMaterialId?: string;
|
||||
|
|
@ -2238,6 +2250,7 @@ interface TeamRuntimeLaunchArgsPlan {
|
|||
providerArgs: string[];
|
||||
extraArgs: string[];
|
||||
inheritedProviderArgs: string[];
|
||||
appManagedSettingsPath: string | null;
|
||||
}
|
||||
|
||||
type WorkspaceTrustProviderArgsResolver = (input: {
|
||||
|
|
@ -4259,6 +4272,7 @@ export class TeamProvisioningService {
|
|||
providerArgs: rawProviderArgs,
|
||||
extraArgs: rawExtraArgs,
|
||||
inheritedProviderArgs: rawInheritedProviderArgs,
|
||||
appManagedSettingsPath: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -4313,6 +4327,7 @@ export class TeamProvisioningService {
|
|||
providerArgs: splitProviderArgs.passthroughArgs,
|
||||
extraArgs: splitExtraArgs.passthroughArgs,
|
||||
inheritedProviderArgs: splitInheritedArgs.passthroughArgs,
|
||||
appManagedSettingsPath: settingsBundle?.settingsPath ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -14819,6 +14834,10 @@ export class TeamProvisioningService {
|
|||
includeAnthropicHelper: providerId === 'anthropic',
|
||||
contextLabel: `Direct teammate restart (${input.configuredMember.name})`,
|
||||
});
|
||||
applyAppManagedRuntimeSettingsPathEnv(
|
||||
provisioningEnv.env,
|
||||
runtimeArgsPlan.appManagedSettingsPath
|
||||
);
|
||||
|
||||
const runtimeArgs = mergeJsonSettingsArgs([
|
||||
'--agent-id',
|
||||
|
|
@ -15010,6 +15029,10 @@ export class TeamProvisioningService {
|
|||
includeAnthropicHelper: providerId === 'anthropic',
|
||||
contextLabel: `Direct process teammate restart (${input.configuredMember.name})`,
|
||||
});
|
||||
applyAppManagedRuntimeSettingsPathEnv(
|
||||
provisioningEnv.env,
|
||||
runtimeArgsPlan.appManagedSettingsPath
|
||||
);
|
||||
|
||||
const runtimeArgs = mergeJsonSettingsArgs([
|
||||
'--teammate-runtime',
|
||||
|
|
@ -20257,6 +20280,7 @@ export class TeamProvisioningService {
|
|||
...runtimeArgsPlan.settingsArgs,
|
||||
...runtimeArgsPlan.inheritedProviderArgs,
|
||||
]);
|
||||
applyAppManagedRuntimeSettingsPathEnv(shellEnv, runtimeArgsPlan.appManagedSettingsPath);
|
||||
const runtimeWarning = buildRuntimeLaunchWarning(request, shellEnv, {
|
||||
geminiRuntimeAuth,
|
||||
promptSize,
|
||||
|
|
@ -21575,6 +21599,7 @@ export class TeamProvisioningService {
|
|||
emitProvisioningCheckpoint(run, 'Resolving cross-provider member launch args');
|
||||
launchArgs.push(...runtimeArgsPlan.inheritedProviderArgs);
|
||||
const finalLaunchArgs = mergeJsonSettingsArgs(launchArgs);
|
||||
applyAppManagedRuntimeSettingsPathEnv(shellEnv, runtimeArgsPlan.appManagedSettingsPath);
|
||||
const runtimeWarning = buildRuntimeLaunchWarning(request, shellEnv, {
|
||||
geminiRuntimeAuth,
|
||||
promptSize,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import type { TeamProviderId } from '@shared/types';
|
|||
const DIRECT_TMUX_RESTART_ENV_KEYS = [
|
||||
'CLAUDE_CONFIG_DIR',
|
||||
'CLAUDE_TEAM_CONTROL_URL',
|
||||
'CLAUDE_TEAM_RUNTIME_SETTINGS_PATH',
|
||||
'CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST',
|
||||
'CLAUDE_CODE_USE_OPENAI',
|
||||
'CLAUDE_CODE_USE_BEDROCK',
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ describe('TeamProvisioningDirectRestart', () => {
|
|||
CLAUDE_CODE_ENTRY_PROVIDER: 'gemini',
|
||||
CLAUDE_CODE_CODEX_BACKEND: 'codex-native',
|
||||
CLAUDE_CODE_CODEX_FORCED_LOGIN_METHOD: 'chatgpt',
|
||||
CLAUDE_TEAM_RUNTIME_SETTINGS_PATH: '/tmp/runtime-settings.json',
|
||||
},
|
||||
'codex'
|
||||
);
|
||||
|
|
@ -74,6 +75,7 @@ describe('TeamProvisioningDirectRestart', () => {
|
|||
expect(assignments).toContain("CLAUDE_CODE_ENTRY_PROVIDER='codex'");
|
||||
expect(assignments).toContain("CLAUDE_CODE_CODEX_BACKEND='codex-native'");
|
||||
expect(assignments).toContain("CLAUDE_CODE_CODEX_FORCED_LOGIN_METHOD='chatgpt'");
|
||||
expect(assignments).toContain("CLAUDE_TEAM_RUNTIME_SETTINGS_PATH='/tmp/runtime-settings.json'");
|
||||
expect(assignments).toContain("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST='1'");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -531,6 +531,7 @@ describe('TeamProvisioningService member MCP config safe e2e', () => {
|
|||
settingsArgs: string[];
|
||||
extraArgs: string[];
|
||||
inheritedProviderArgs: string[];
|
||||
appManagedSettingsPath: string | null;
|
||||
}>;
|
||||
}
|
||||
).buildTeamRuntimeLaunchArgsPlan = vi.fn(async () => ({
|
||||
|
|
@ -540,6 +541,7 @@ describe('TeamProvisioningService member MCP config safe e2e', () => {
|
|||
settingsArgs: [],
|
||||
extraArgs: [],
|
||||
inheritedProviderArgs: [],
|
||||
appManagedSettingsPath: null,
|
||||
}));
|
||||
(
|
||||
svc as unknown as { updateDirectTmuxRestartMemberConfig: () => Promise<void> }
|
||||
|
|
|
|||
|
|
@ -15538,6 +15538,7 @@ describe('TeamProvisioningService', () => {
|
|||
settingsArgs: [],
|
||||
extraArgs: [],
|
||||
inheritedProviderArgs: [],
|
||||
appManagedSettingsPath: null,
|
||||
}));
|
||||
(svc as any).materializeDirectProcessNativeBootstrapContext = vi.fn(async () => ({}));
|
||||
(svc as any).updateDirectTmuxRestartMemberConfig = vi.fn(async () => {});
|
||||
|
|
@ -15637,6 +15638,7 @@ describe('TeamProvisioningService', () => {
|
|||
settingsArgs: [],
|
||||
extraArgs: [],
|
||||
inheritedProviderArgs: [],
|
||||
appManagedSettingsPath: null,
|
||||
}));
|
||||
(svc as any).materializeDirectProcessNativeBootstrapContext = vi.fn(async () => ({}));
|
||||
(svc as any).updateDirectTmuxRestartMemberConfig = vi.fn(async () => {});
|
||||
|
|
|
|||
|
|
@ -3566,6 +3566,7 @@ describe('TeamProvisioningService prepare/auth behavior', () => {
|
|||
|
||||
expect(result.settingsArgs[0]).toBe('--settings');
|
||||
expect(result.inheritedProviderArgs).toEqual([]);
|
||||
expect(result.appManagedSettingsPath).toBe(result.settingsArgs[1]);
|
||||
const settings = JSON.parse(fs.readFileSync(result.settingsArgs[1], 'utf8'));
|
||||
expect(settings.codex.forced_login_method).toBe('chatgpt');
|
||||
});
|
||||
|
|
@ -3651,6 +3652,7 @@ describe('TeamProvisioningService prepare/auth behavior', () => {
|
|||
|
||||
expect(result.settingsArgs).toEqual([]);
|
||||
expect(result.inheritedProviderArgs).toEqual(inheritedProviderArgs);
|
||||
expect(result.appManagedSettingsPath).toBeNull();
|
||||
});
|
||||
|
||||
it('coalesces inherited JSON settings into Anthropic helper settings without keeping helper path args', async () => {
|
||||
|
|
@ -3685,6 +3687,7 @@ describe('TeamProvisioningService prepare/auth behavior', () => {
|
|||
expect(result.settingsArgs[0]).toBe('--settings');
|
||||
expect(result.settingsArgs[1]).toContain(helperDir);
|
||||
expect(result.settingsArgs[1]).not.toBe(helperSettingsPath);
|
||||
expect(result.appManagedSettingsPath).toBe(result.settingsArgs[1]);
|
||||
const settings = JSON.parse(fs.readFileSync(result.settingsArgs[1], 'utf8'));
|
||||
expect(settings.apiKeyHelper).toBe(`'${path.join(helperDir, 'helper.sh')}'`);
|
||||
expect(settings.codex.forced_login_method).toBe('chatgpt');
|
||||
|
|
|
|||
|
|
@ -178,6 +178,16 @@ function readRuntimeSettingsFromLaunchArgs(callIndex = 0): Record<string, unknow
|
|||
return JSON.parse(fs.readFileSync(settingsValue, 'utf8')) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function readRuntimeSettingsPathFromLaunchArgs(callIndex = 0): string {
|
||||
const args = vi.mocked(spawnCli).mock.calls[callIndex]?.[1] as string[] | undefined;
|
||||
const settingsFlagIndex = args?.indexOf('--settings') ?? -1;
|
||||
const settingsValue = settingsFlagIndex >= 0 ? args?.[settingsFlagIndex + 1] : null;
|
||||
if (!settingsValue || settingsValue.trim().startsWith('{')) {
|
||||
throw new Error('Failed to extract runtime settings path from spawn args');
|
||||
}
|
||||
return settingsValue;
|
||||
}
|
||||
|
||||
function registerNoopOpenCodeRuntimeAdapter(svc: TeamProvisioningService): void {
|
||||
svc.setRuntimeAdapterRegistry(
|
||||
new TeamRuntimeAdapterRegistry([
|
||||
|
|
@ -564,6 +574,9 @@ describe('TeamProvisioningService prompt content (solo mode discipline)', () =>
|
|||
expect(extractBootstrapSpec().members).toEqual([
|
||||
expect.objectContaining({ name: 'alice', provider: 'codex' }),
|
||||
]);
|
||||
const settingsPath = readRuntimeSettingsPathFromLaunchArgs();
|
||||
const launchEnv = vi.mocked(spawnCli).mock.calls[0]?.[2]?.env as NodeJS.ProcessEnv;
|
||||
expect(launchEnv.CLAUDE_TEAM_RUNTIME_SETTINGS_PATH).toBe(settingsPath);
|
||||
const settings = readRuntimeSettingsFromLaunchArgs();
|
||||
expect((settings.codex as Record<string, unknown>).forced_login_method).toBe('chatgpt');
|
||||
|
||||
|
|
@ -1260,6 +1273,9 @@ describe('TeamProvisioningService prompt content (solo mode discipline)', () =>
|
|||
expect(extractBootstrapSpec().members).toEqual([
|
||||
expect.objectContaining({ name: 'alice', provider: 'codex' }),
|
||||
]);
|
||||
const settingsPath = readRuntimeSettingsPathFromLaunchArgs();
|
||||
const launchEnv = vi.mocked(spawnCli).mock.calls[0]?.[2]?.env as NodeJS.ProcessEnv;
|
||||
expect(launchEnv.CLAUDE_TEAM_RUNTIME_SETTINGS_PATH).toBe(settingsPath);
|
||||
const settings = readRuntimeSettingsFromLaunchArgs();
|
||||
expect((settings.codex as Record<string, unknown>).forced_login_method).toBe('chatgpt');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue