fix(team): surface permission-blocked launch state in graph

This commit is contained in:
777genius 2026-04-23 01:13:17 +03:00
parent cfc50df5a1
commit a123b2e247
2 changed files with 36 additions and 6 deletions

View file

@ -518,7 +518,7 @@ export class TeamGraphAdapter {
label: member.name,
state: hasRunningTool
? 'tool_calling'
: TeamGraphAdapter.#mapMemberStatus(member.status, spawn?.status),
: TeamGraphAdapter.#mapMemberStatus(member.status, spawn),
color: member.color ?? undefined,
role: member.role ?? undefined,
runtimeLabel: TeamGraphAdapter.#getRuntimeLabel(
@ -1128,7 +1128,7 @@ export class TeamGraphAdapter {
if (spawn?.launchState === 'failed_to_start' || spawn?.status === 'error') {
return { exceptionTone: 'error', exceptionLabel: 'spawn failed' };
}
if (pendingApproval) {
if (pendingApproval || spawn?.launchState === 'runtime_pending_permission') {
return { exceptionTone: 'warning', exceptionLabel: 'awaiting approval' };
}
if (spawn?.status === 'waiting' || spawn?.status === 'spawning') {
@ -1144,10 +1144,11 @@ export class TeamGraphAdapter {
return undefined;
}
static #mapMemberStatus(status: string, spawnStatus?: string): GraphNodeState {
if (spawnStatus === 'spawning') return 'thinking';
if (spawnStatus === 'error') return 'error';
if (spawnStatus === 'waiting') return 'waiting';
static #mapMemberStatus(status: string, spawn?: MemberSpawnStatusEntry): GraphNodeState {
if (spawn?.launchState === 'runtime_pending_permission') return 'waiting';
if (spawn?.status === 'spawning') return 'thinking';
if (spawn?.status === 'error') return 'error';
if (spawn?.status === 'waiting') return 'waiting';
switch (status) {
case 'active':
return 'active';

View file

@ -1360,6 +1360,35 @@ describe('TeamGraphAdapter particles', () => {
});
});
it('treats permission-blocked spawn state as awaiting approval even without pending approval feed', () => {
const adapter = TeamGraphAdapter.create();
const teamData = createBaseTeamData();
adapter.adapt(teamData, 'my-team');
const graph = adapter.adapt(teamData, 'my-team', {
alice: {
status: 'online',
launchState: 'runtime_pending_permission',
runtimeAlive: true,
agentToolAccepted: true,
bootstrapConfirmed: false,
hardFailure: false,
updatedAt: '2026-04-08T20:00:00.000Z',
},
});
expect(findNode(graph, 'member:my-team:alice')).toMatchObject({
state: 'waiting',
spawnStatus: 'online',
launchVisualState: 'permission_pending',
launchStatusLabel: 'awaiting permission',
exceptionTone: 'warning',
exceptionLabel: 'awaiting approval',
pendingApproval: false,
});
});
it('refreshes unread comment badges when comment read state changes without task changes', () => {
const adapter = TeamGraphAdapter.create();
const teamData = createBaseTeamData({