fix(team): seed Write/Edit/NotebookEdit permissions before CLI launch
seedTeammateOperationalPermissionRules already pre-writes MCP tool rules to settings.local.json before spawning the CLI. But standard file tools (Write, Edit, NotebookEdit) were missing. FACT: Teammates requesting Write get setMode: acceptEdits suggestion but we can't change subprocess session mode. Pre-seeding these tools as allow rules prevents the permission prompt entirely.
This commit is contained in:
parent
bd242fac5a
commit
f7876d89f7
2 changed files with 28 additions and 7 deletions
|
|
@ -6541,11 +6541,16 @@ export class TeamProvisioningService {
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const settingsPath = path.join(projectCwd, '.claude', 'settings.local.json');
|
const settingsPath = path.join(projectCwd, '.claude', 'settings.local.json');
|
||||||
try {
|
try {
|
||||||
const added = await this.addPermissionRulesToSettings(
|
// FACT: Teammates need both MCP tools AND standard file tools (Write/Edit).
|
||||||
settingsPath,
|
// FACT: Standard tools use "setMode: acceptEdits" permission_suggestions, but
|
||||||
[...AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES],
|
// we can't change subprocess session mode — so we pre-add them as allow rules.
|
||||||
'allow'
|
const allTools = [
|
||||||
);
|
...AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES,
|
||||||
|
'Edit',
|
||||||
|
'Write',
|
||||||
|
'NotebookEdit',
|
||||||
|
];
|
||||||
|
const added = await this.addPermissionRulesToSettings(settingsPath, allTools, 'allow');
|
||||||
logger.info(
|
logger.info(
|
||||||
`[${teamName}] Seeded teammate operational MCP rules in ${settingsPath} (${added} added)`
|
`[${teamName}] Seeded teammate operational MCP rules in ${settingsPath} (${added} added)`
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
|
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
|
||||||
|
import { getInboxJsonType, isInboxNoiseMessage } from '@shared/utils/inboxNoise';
|
||||||
import { isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -524,6 +525,15 @@ export class TeamGraphAdapter {
|
||||||
// Skip comment notifications — #buildCommentParticles handles them with real text
|
// Skip comment notifications — #buildCommentParticles handles them with real text
|
||||||
if (msg.summary?.startsWith('Comment on ')) continue;
|
if (msg.summary?.startsWith('Comment on ')) continue;
|
||||||
|
|
||||||
|
// Handle noise messages: idle shows as "idle", others (shutdown, terminated) skip entirely
|
||||||
|
const msgText = msg.text ?? '';
|
||||||
|
const noiseType = getInboxJsonType(msgText);
|
||||||
|
if (noiseType === 'idle_notification') {
|
||||||
|
// Show idle as a simple label, don't skip
|
||||||
|
} else if (isInboxNoiseMessage(msgText)) {
|
||||||
|
continue; // skip shutdown_approved, teammate_terminated, shutdown_request
|
||||||
|
}
|
||||||
|
|
||||||
const edgeId = TeamGraphAdapter.#resolveMessageEdge(msg, teamName, leadId, leadName, edges);
|
const edgeId = TeamGraphAdapter.#resolveMessageEdge(msg, teamName, leadId, leadName, edges);
|
||||||
if (!edgeId) continue;
|
if (!edgeId) continue;
|
||||||
|
|
||||||
|
|
@ -537,13 +547,19 @@ export class TeamGraphAdapter {
|
||||||
);
|
);
|
||||||
const isFromTeammate = fromId !== leadId;
|
const isFromTeammate = fromId !== leadId;
|
||||||
|
|
||||||
|
// For idle notifications, show a clean "idle" label instead of raw JSON
|
||||||
|
const particleLabel =
|
||||||
|
noiseType === 'idle_notification'
|
||||||
|
? 'idle'
|
||||||
|
: TeamGraphAdapter.#buildParticleLabel(msg.summary ?? msg.text, 'inbox');
|
||||||
|
|
||||||
particles.push({
|
particles.push({
|
||||||
id: `particle:msg:${teamName}:${msgKey}`,
|
id: `particle:msg:${teamName}:${msgKey}`,
|
||||||
edgeId,
|
edgeId,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
kind: 'inbox_message',
|
kind: 'inbox_message',
|
||||||
color: msg.color ?? '#66ccff',
|
color: msg.color ?? '#66ccff',
|
||||||
label: TeamGraphAdapter.#buildParticleLabel(msg.summary ?? msg.text, 'inbox'),
|
label: particleLabel,
|
||||||
reverse: isFromTeammate,
|
reverse: isFromTeammate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -735,7 +751,7 @@ export class TeamGraphAdapter {
|
||||||
static #buildParticleLabel(
|
static #buildParticleLabel(
|
||||||
text: string | undefined,
|
text: string | undefined,
|
||||||
kind: 'inbox' | 'comment',
|
kind: 'inbox' | 'comment',
|
||||||
max = 26
|
max = 52
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
const normalized = text?.replace(/\s+/g, ' ').trim();
|
const normalized = text?.replace(/\s+/g, ' ').trim();
|
||||||
const prefix = kind === 'comment' ? '\u{1F4AC}' : '\u{2709}';
|
const prefix = kind === 'comment' ? '\u{1F4AC}' : '\u{2709}';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue