@@ -31999,103 +31999,100 @@ export class TeamProvisioningService {
3199931999 });
3200032000 };
3200132001
32002- // Apply permission_suggestions: add tool rules to project settings file
32002+ // Apply permission_suggestions: add tool rules to project settings file.
3200332003 if (suggestions.length === 0) {
32004- sendSuccessResponse();
3200532004 logger.info(
32006- `[${run.teamName}] No permission_suggestions for ${requestId}; inbox response sent `
32005+ `[${run.teamName}] No permission_suggestions for ${requestId}; sending allow responses only `
3200732006 );
32008- return;
32009- }
32007+ } else {
32008+ // Resolve project cwd from team config
32009+ let projectCwd: string | undefined;
32010+ try {
32011+ const config = await this.readConfigForStrictDecision(run.teamName);
32012+ projectCwd = config?.projectPath ?? config?.members?.[0]?.cwd;
32013+ } catch {
32014+ // best-effort
32015+ }
3201032016
32011- // Resolve project cwd from team config
32012- let projectCwd: string | undefined;
32013- try {
32014- const config = await this.readConfigForStrictDecision(run.teamName);
32015- projectCwd = config?.projectPath ?? config?.members?.[0]?.cwd;
32016- } catch {
32017- // best-effort
32018- }
32019- if (!projectCwd) {
32020- logger.warn(
32021- `[${run.teamName}] Cannot resolve project cwd for permission rule; sending inbox response only`
32022- );
32023- sendSuccessResponse();
32024- return;
32025- }
32017+ if (!projectCwd) {
32018+ logger.warn(
32019+ `[${run.teamName}] Cannot resolve project cwd for permission rule; sending allow responses only`
32020+ );
32021+ } else {
32022+ for (const suggestion of suggestions) {
32023+ // Handle "setMode" suggestions (e.g. Write/Edit tools suggest acceptEdits mode)
32024+ // FACT: Write/Edit permission_requests have permission_suggestions:
32025+ // { type: "setMode", mode: "acceptEdits", destination: "session" }
32026+ // Since we can't change session mode of a subprocess, we translate to addRules.
32027+ if (suggestion.type === 'setMode') {
32028+ const mode = typeof suggestion.mode === 'string' ? suggestion.mode : '';
32029+ let toolNames: string[] = [];
32030+ if (mode === 'acceptEdits') {
32031+ toolNames = ['Edit', 'Write', 'NotebookEdit'];
32032+ } else if (mode === 'bypassPermissions') {
32033+ // Broad approval - add common tools
32034+ toolNames = ['Edit', 'Write', 'NotebookEdit', 'Bash', 'Read', 'Grep', 'Glob'];
32035+ }
32036+ if (toolNames.length > 0) {
32037+ const settingsPath = path.join(projectCwd, '.claude', 'settings.local.json');
32038+ try {
32039+ await this.addPermissionRulesToSettings(settingsPath, toolNames, 'allow');
32040+ logger.info(
32041+ `[${run.teamName}] Applied setMode "${mode}" for ${agentId}: ${toolNames.join(', ')} in ${settingsPath}`
32042+ );
32043+ } catch (error) {
32044+ logger.error(
32045+ `[${run.teamName}] Failed to apply setMode: ${
32046+ error instanceof Error ? error.message : String(error)
32047+ }`
32048+ );
32049+ }
32050+ }
32051+ continue;
32052+ }
32053+
32054+ if (suggestion.type !== 'addRules' || !Array.isArray(suggestion.rules)) continue;
32055+
32056+ let toolNames = suggestion.rules
32057+ .map((r) => r.toolName)
32058+ .filter((name): name is string => typeof name === 'string' && name.length > 0);
32059+ if (toolNames.length === 0) continue;
32060+
32061+ // Expand teammate-safe operational tools only.
32062+ // This removes the bootstrap/task workflow race without accidentally granting
32063+ // admin/runtime tools like team_stop or kanban_clear.
32064+ if (
32065+ toolNames.some((name) =>
32066+ AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES.includes(name)
32067+ )
32068+ ) {
32069+ const merged = new Set([
32070+ ...toolNames,
32071+ ...AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES,
32072+ ]);
32073+ toolNames = Array.from(merged);
32074+ }
32075+
32076+ const behavior = suggestion.behavior ?? 'allow';
32077+ // FACT: observed destinations are "localSettings" (project-level .claude/settings.local.json)
32078+ const settingsPath =
32079+ suggestion.destination === 'localSettings'
32080+ ? path.join(projectCwd, '.claude', 'settings.local.json')
32081+ : path.join(projectCwd, '.claude', 'settings.local.json'); // default to local
3202632082
32027- for (const suggestion of suggestions) {
32028- // Handle "setMode" suggestions (e.g. Write/Edit tools suggest acceptEdits mode)
32029- // FACT: Write/Edit permission_requests have permission_suggestions:
32030- // { type: "setMode", mode: "acceptEdits", destination: "session" }
32031- // Since we can't change session mode of a subprocess, we translate to addRules.
32032- if (suggestion.type === 'setMode') {
32033- const mode = typeof suggestion.mode === 'string' ? suggestion.mode : '';
32034- let toolNames: string[] = [];
32035- if (mode === 'acceptEdits') {
32036- toolNames = ['Edit', 'Write', 'NotebookEdit'];
32037- } else if (mode === 'bypassPermissions') {
32038- // Broad approval - add common tools
32039- toolNames = ['Edit', 'Write', 'NotebookEdit', 'Bash', 'Read', 'Grep', 'Glob'];
32040- }
32041- if (toolNames.length > 0) {
32042- const settingsPath = path.join(projectCwd, '.claude', 'settings.local.json');
3204332083 try {
32044- await this.addPermissionRulesToSettings(settingsPath, toolNames, 'allow' );
32084+ await this.addPermissionRulesToSettings(settingsPath, toolNames, behavior );
3204532085 logger.info(
32046- `[${run.teamName}] Applied setMode "${mode}" for ${agentId}: ${toolNames.join(', ')} in ${settingsPath}`
32086+ `[${run.teamName}] Added permission rules for ${agentId}: ${toolNames.join(', ')} -> ${behavior } in ${settingsPath}`
3204732087 );
3204832088 } catch (error) {
3204932089 logger.error(
32050- `[${run.teamName}] Failed to apply setMode : ${
32090+ `[${run.teamName}] Failed to add permission rules : ${
3205132091 error instanceof Error ? error.message : String(error)
3205232092 }`
3205332093 );
3205432094 }
3205532095 }
32056- continue;
32057- }
32058-
32059- if (suggestion.type !== 'addRules' || !Array.isArray(suggestion.rules)) continue;
32060-
32061- let toolNames = suggestion.rules
32062- .map((r) => r.toolName)
32063- .filter((name): name is string => typeof name === 'string' && name.length > 0);
32064- if (toolNames.length === 0) continue;
32065-
32066- // Expand teammate-safe operational tools only.
32067- // This removes the bootstrap/task workflow race without accidentally granting
32068- // admin/runtime tools like team_stop or kanban_clear.
32069- if (
32070- toolNames.some((name) =>
32071- AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES.includes(name)
32072- )
32073- ) {
32074- const merged = new Set([
32075- ...toolNames,
32076- ...AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES,
32077- ]);
32078- toolNames = Array.from(merged);
32079- }
32080-
32081- const behavior = suggestion.behavior ?? 'allow';
32082- // FACT: observed destinations are "localSettings" (project-level .claude/settings.local.json)
32083- const settingsPath =
32084- suggestion.destination === 'localSettings'
32085- ? path.join(projectCwd, '.claude', 'settings.local.json')
32086- : path.join(projectCwd, '.claude', 'settings.local.json'); // default to local
32087-
32088- try {
32089- await this.addPermissionRulesToSettings(settingsPath, toolNames, behavior);
32090- logger.info(
32091- `[${run.teamName}] Added permission rules for ${agentId}: ${toolNames.join(', ')} -> ${behavior} in ${settingsPath}`
32092- );
32093- } catch (error) {
32094- logger.error(
32095- `[${run.teamName}] Failed to add permission rules: ${
32096- error instanceof Error ? error.message : String(error)
32097- }`
32098- );
3209932096 }
3210032097 }
3210132098
@@ -32186,7 +32183,7 @@ export class TeamProvisioningService {
3218632183 message: string | undefined
3218732184 ): Record<string, unknown> | undefined {
3218832185 if (!toolInput) return undefined;
32189- if (toolName !== 'AskUserQuestion' || ! message) return toolInput;
32186+ if (toolName !== 'AskUserQuestion' || message === undefined ) return toolInput;
3219032187
3219132188 const answers = this.parseAskUserQuestionAnswers(message, toolInput);
3219232189 return Object.keys(answers).length > 0 ? { ...toolInput, answers } : toolInput;
0 commit comments