fix(team): render agent error messages
* fix(team): render agent error messages * test(team): cover agent error activity rendering * fix(ci): clear ui lint gate * test(team): reset config cache in relay suites * test(team): harden mixed lane matrix waits * test(team): harden ci-sensitive team assertions --------- Co-authored-by: iliya <iliyazelenkog@gmail.com> Co-authored-by: 777genius <quantjumppro@gmail.com>
This commit is contained in:
parent
8f1ee5603c
commit
ca21ab206e
15 changed files with 105 additions and 23 deletions
|
|
@ -179,6 +179,11 @@ export class TeamConfigReader {
|
||||||
private static readonly configCacheByPath = new Map<string, CachedTeamConfig>();
|
private static readonly configCacheByPath = new Map<string, CachedTeamConfig>();
|
||||||
private static readonly configReadInFlightByPath = new Map<string, Promise<TeamConfig | null>>();
|
private static readonly configReadInFlightByPath = new Map<string, Promise<TeamConfig | null>>();
|
||||||
|
|
||||||
|
static clearCacheForTests(): void {
|
||||||
|
TeamConfigReader.configCacheByPath.clear();
|
||||||
|
TeamConfigReader.configReadInFlightByPath.clear();
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly membersMetaStore: TeamMembersMetaStore = new TeamMembersMetaStore(),
|
private readonly membersMetaStore: TeamMembersMetaStore = new TeamMembersMetaStore(),
|
||||||
private readonly teamMetaStore: TeamMetaStore = new TeamMetaStore()
|
private readonly teamMetaStore: TeamMetaStore = new TeamMetaStore()
|
||||||
|
|
@ -553,8 +558,6 @@ export class TeamConfigReader {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.resolveConfigRead(teamName, configPath, readPromise);
|
return await this.resolveConfigRead(teamName, configPath, readPromise);
|
||||||
} catch (error) {
|
|
||||||
return null;
|
|
||||||
} finally {
|
} finally {
|
||||||
if (TeamConfigReader.configReadInFlightByPath.get(configPath) === readPromise) {
|
if (TeamConfigReader.configReadInFlightByPath.get(configPath) === readPromise) {
|
||||||
TeamConfigReader.configReadInFlightByPath.delete(configPath);
|
TeamConfigReader.configReadInFlightByPath.delete(configPath);
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,9 @@ export class TeamInboxReader {
|
||||||
messageKind:
|
messageKind:
|
||||||
row.messageKind === 'slash_command' ||
|
row.messageKind === 'slash_command' ||
|
||||||
row.messageKind === 'slash_command_result' ||
|
row.messageKind === 'slash_command_result' ||
|
||||||
row.messageKind === 'task_comment_notification'
|
row.messageKind === 'task_comment_notification' ||
|
||||||
|
row.messageKind === 'member_work_sync_nudge' ||
|
||||||
|
row.messageKind === 'agent_error'
|
||||||
? row.messageKind
|
? row.messageKind
|
||||||
: row.messageKind === 'default'
|
: row.messageKind === 'default'
|
||||||
? 'default'
|
? 'default'
|
||||||
|
|
|
||||||
|
|
@ -168,10 +168,6 @@ import {
|
||||||
setOpenCodeRuntimeActiveRunManifest,
|
setOpenCodeRuntimeActiveRunManifest,
|
||||||
upsertOpenCodeRuntimeLaneIndexEntry,
|
upsertOpenCodeRuntimeLaneIndexEntry,
|
||||||
} from './opencode/store/OpenCodeRuntimeManifestEvidenceReader';
|
} from './opencode/store/OpenCodeRuntimeManifestEvidenceReader';
|
||||||
import type {
|
|
||||||
OpenCodeCommittedBootstrapSessionRecord,
|
|
||||||
OpenCodeRuntimeLaneIndexEntry,
|
|
||||||
} from './opencode/store/OpenCodeRuntimeManifestEvidenceReader';
|
|
||||||
import {
|
import {
|
||||||
createRuntimeRunTombstoneStore,
|
createRuntimeRunTombstoneStore,
|
||||||
type RuntimeEvidenceKind,
|
type RuntimeEvidenceKind,
|
||||||
|
|
@ -242,6 +238,10 @@ import { TeamSentMessagesStore } from './TeamSentMessagesStore';
|
||||||
import { TeamTaskReader } from './TeamTaskReader';
|
import { TeamTaskReader } from './TeamTaskReader';
|
||||||
import { TeamTranscriptProjectResolver } from './TeamTranscriptProjectResolver';
|
import { TeamTranscriptProjectResolver } from './TeamTranscriptProjectResolver';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
OpenCodeCommittedBootstrapSessionRecord,
|
||||||
|
OpenCodeRuntimeLaneIndexEntry,
|
||||||
|
} from './opencode/store/OpenCodeRuntimeManifestEvidenceReader';
|
||||||
import type {
|
import type {
|
||||||
OpenCodeTeamRuntimeMessageInput,
|
OpenCodeTeamRuntimeMessageInput,
|
||||||
OpenCodeTeamRuntimeMessageResult,
|
OpenCodeTeamRuntimeMessageResult,
|
||||||
|
|
|
||||||
|
|
@ -59,9 +59,9 @@ export { TaskBoundaryParser } from './TaskBoundaryParser';
|
||||||
export { BoardTaskActivityDetailService } from './taskLogs/activity/BoardTaskActivityDetailService';
|
export { BoardTaskActivityDetailService } from './taskLogs/activity/BoardTaskActivityDetailService';
|
||||||
export { BoardTaskActivityRecordSource } from './taskLogs/activity/BoardTaskActivityRecordSource';
|
export { BoardTaskActivityRecordSource } from './taskLogs/activity/BoardTaskActivityRecordSource';
|
||||||
export { BoardTaskActivityService } from './taskLogs/activity/BoardTaskActivityService';
|
export { BoardTaskActivityService } from './taskLogs/activity/BoardTaskActivityService';
|
||||||
|
export { TeamTranscriptSourceLocator } from './taskLogs/discovery/TeamTranscriptSourceLocator';
|
||||||
export { BoardTaskExactLogDetailService } from './taskLogs/exact/BoardTaskExactLogDetailService';
|
export { BoardTaskExactLogDetailService } from './taskLogs/exact/BoardTaskExactLogDetailService';
|
||||||
export { BoardTaskExactLogsService } from './taskLogs/exact/BoardTaskExactLogsService';
|
export { BoardTaskExactLogsService } from './taskLogs/exact/BoardTaskExactLogsService';
|
||||||
export { TeamTranscriptSourceLocator } from './taskLogs/discovery/TeamTranscriptSourceLocator';
|
|
||||||
export { BoardTaskLogStreamService } from './taskLogs/stream/BoardTaskLogStreamService';
|
export { BoardTaskLogStreamService } from './taskLogs/stream/BoardTaskLogStreamService';
|
||||||
export type {
|
export type {
|
||||||
OpenCodeTaskLogAttributionBulkWriteOutcome,
|
OpenCodeTaskLogAttributionBulkWriteOutcome,
|
||||||
|
|
|
||||||
|
|
@ -85,11 +85,11 @@ const EMPTY_TEAM_COLOR_MAP = new Map<string, string>();
|
||||||
const NOOP_TEAM_CLICK = (): void => undefined;
|
const NOOP_TEAM_CLICK = (): void => undefined;
|
||||||
|
|
||||||
type ViewerMarkdownMode = 'default' | 'compact-preview';
|
type ViewerMarkdownMode = 'default' | 'compact-preview';
|
||||||
type HastElementLike = {
|
interface HastElementLike {
|
||||||
tagName?: string;
|
tagName?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
children?: unknown[];
|
children?: unknown[];
|
||||||
};
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
|
||||||
|
|
@ -681,7 +681,7 @@ export const ActivityItem = memo(
|
||||||
// Only flag agent messages as rate-limited, not user's own quotes
|
// Only flag agent messages as rate-limited, not user's own quotes
|
||||||
const rateLimited = message.from !== 'user' && isRateLimitMessage(message.text);
|
const rateLimited = message.from !== 'user' && isRateLimitMessage(message.text);
|
||||||
// Highlight messages containing API errors
|
// Highlight messages containing API errors
|
||||||
const isApiError = message.text.includes('API Error');
|
const isApiError = message.messageKind === 'agent_error' || message.text.includes('API Error');
|
||||||
// Detect auth errors that may be resolved by restarting the team
|
// Detect auth errors that may be resolved by restarting the team
|
||||||
const isAuthError = isApiError && AUTH_ERROR_PATTERNS.some((p) => p.test(message.text));
|
const isAuthError = isApiError && AUTH_ERROR_PATTERNS.some((p) => p.test(message.text));
|
||||||
// Never collapse rate limit messages as noise — they must be visible
|
// Never collapse rate limit messages as noise — they must be visible
|
||||||
|
|
@ -1029,7 +1029,7 @@ export const ActivityItem = memo(
|
||||||
) : isApiError ? (
|
) : isApiError ? (
|
||||||
<span className="inline-flex items-center gap-1 rounded-full bg-red-500/20 px-1.5 py-0.5 text-[10px] font-medium text-red-400">
|
<span className="inline-flex items-center gap-1 rounded-full bg-red-500/20 px-1.5 py-0.5 text-[10px] font-medium text-red-400">
|
||||||
<AlertTriangle size={10} />
|
<AlertTriangle size={10} />
|
||||||
API Error
|
{message.messageKind === 'agent_error' ? 'Agent Error' : 'API Error'}
|
||||||
</span>
|
</span>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,13 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@renderer/components/u
|
||||||
import { useMemberStats } from '@renderer/hooks/useMemberStats';
|
import { useMemberStats } from '@renderer/hooks/useMemberStats';
|
||||||
import { useStore } from '@renderer/store';
|
import { useStore } from '@renderer/store';
|
||||||
import { selectMemberMessagesForTeamMember } from '@renderer/store/slices/teamSlice';
|
import { selectMemberMessagesForTeamMember } from '@renderer/store/slices/teamSlice';
|
||||||
|
import { isOpenCodeRelaunchActionable } from '@renderer/utils/memberHelpers';
|
||||||
import {
|
import {
|
||||||
buildMemberLaunchDiagnosticsPayload,
|
buildMemberLaunchDiagnosticsPayload,
|
||||||
getMemberLaunchDiagnosticsErrorMessage,
|
getMemberLaunchDiagnosticsErrorMessage,
|
||||||
hasMemberLaunchDiagnosticsDetails,
|
hasMemberLaunchDiagnosticsDetails,
|
||||||
hasMemberLaunchDiagnosticsError,
|
hasMemberLaunchDiagnosticsError,
|
||||||
} from '@renderer/utils/memberLaunchDiagnostics';
|
} from '@renderer/utils/memberLaunchDiagnostics';
|
||||||
import { isOpenCodeRelaunchActionable } from '@renderer/utils/memberHelpers';
|
|
||||||
import {
|
import {
|
||||||
getRuntimeMemorySourceLabel,
|
getRuntimeMemorySourceLabel,
|
||||||
resolveMemberRuntimeSummary,
|
resolveMemberRuntimeSummary,
|
||||||
|
|
|
||||||
|
|
@ -414,7 +414,8 @@ export type InboxMessageKind =
|
||||||
| 'slash_command'
|
| 'slash_command'
|
||||||
| 'slash_command_result'
|
| 'slash_command_result'
|
||||||
| 'task_comment_notification'
|
| 'task_comment_notification'
|
||||||
| 'member_work_sync_nudge';
|
| 'member_work_sync_nudge'
|
||||||
|
| 'agent_error';
|
||||||
|
|
||||||
export interface SlashCommandMeta {
|
export interface SlashCommandMeta {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import * as path from 'path';
|
||||||
|
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { TeamConfigReader } from '../../../../src/main/services/team/TeamConfigReader';
|
||||||
import { TeamProvisioningService } from '../../../../src/main/services/team/TeamProvisioningService';
|
import { TeamProvisioningService } from '../../../../src/main/services/team/TeamProvisioningService';
|
||||||
import type {
|
import type {
|
||||||
OpenCodeTeamRuntimeMessageInput,
|
OpenCodeTeamRuntimeMessageInput,
|
||||||
|
|
@ -47,6 +48,7 @@ describe('Team agent launch matrix safe e2e', () => {
|
||||||
let projectPath: string;
|
let projectPath: string;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
TeamConfigReader.clearCacheForTests();
|
||||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'agent-launch-matrix-e2e-'));
|
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'agent-launch-matrix-e2e-'));
|
||||||
tempClaudeRoot = path.join(tempDir, '.claude');
|
tempClaudeRoot = path.join(tempDir, '.claude');
|
||||||
projectPath = path.join(tempDir, 'project');
|
projectPath = path.join(tempDir, 'project');
|
||||||
|
|
@ -56,6 +58,7 @@ describe('Team agent launch matrix safe e2e', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
TeamConfigReader.clearCacheForTests();
|
||||||
setClaudeBasePathOverride(null);
|
setClaudeBasePathOverride(null);
|
||||||
await removeTempDirWithRetries(tempDir);
|
await removeTempDirWithRetries(tempDir);
|
||||||
});
|
});
|
||||||
|
|
@ -10066,7 +10069,9 @@ describe('Team agent launch matrix safe e2e', () => {
|
||||||
|
|
||||||
await (svc as any).launchMixedSecondaryLaneIfNeeded(cancelledRun);
|
await (svc as any).launchMixedSecondaryLaneIfNeeded(cancelledRun);
|
||||||
await (svc as any).launchMixedSecondaryLaneIfNeeded(survivingRun);
|
await (svc as any).launchMixedSecondaryLaneIfNeeded(survivingRun);
|
||||||
await waitForCondition(() => adapter.pendingLaunchInputs.length === 1);
|
await waitForCondition(() =>
|
||||||
|
adapter.pendingLaunchInputs.some((input) => input.teamName === cancelledTeamName)
|
||||||
|
);
|
||||||
|
|
||||||
await svc.cancelProvisioning(cancelledRun.runId);
|
await svc.cancelProvisioning(cancelledRun.runId);
|
||||||
|
|
||||||
|
|
@ -10191,7 +10196,9 @@ describe('Team agent launch matrix safe e2e', () => {
|
||||||
|
|
||||||
await (svc as any).launchMixedSecondaryLaneIfNeeded(cancelledRun);
|
await (svc as any).launchMixedSecondaryLaneIfNeeded(cancelledRun);
|
||||||
await (svc as any).launchMixedSecondaryLaneIfNeeded(survivingRun);
|
await (svc as any).launchMixedSecondaryLaneIfNeeded(survivingRun);
|
||||||
await waitForCondition(() => adapter.pendingLaunchInputs.length === 1);
|
await waitForCondition(() =>
|
||||||
|
adapter.pendingLaunchInputs.some((input) => input.teamName === cancelledTeamName)
|
||||||
|
);
|
||||||
|
|
||||||
await svc.cancelProvisioning(cancelledRun.runId);
|
await svc.cancelProvisioning(cancelledRun.runId);
|
||||||
|
|
||||||
|
|
@ -10996,7 +11003,10 @@ describe('Team agent launch matrix safe e2e', () => {
|
||||||
svc.stopTeam(teamName);
|
svc.stopTeam(teamName);
|
||||||
await waitForCondition(() => adapter.stopInputs.length === 2);
|
await waitForCondition(() => adapter.stopInputs.length === 2);
|
||||||
await waitForCondition(() => !svc.isTeamAlive(teamName));
|
await waitForCondition(() => !svc.isTeamAlive(teamName));
|
||||||
expect((await readOpenCodeRuntimeLaneIndex(getTeamsBasePath(), teamName)).lanes).toEqual({});
|
await waitForCondition(async () => {
|
||||||
|
const laneIndex = await readOpenCodeRuntimeLaneIndex(getTeamsBasePath(), teamName);
|
||||||
|
return Object.keys(laneIndex.lanes).length === 0;
|
||||||
|
});
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
svc.deliverOpenCodeMemberMessage(teamName, {
|
svc.deliverOpenCodeMemberMessage(teamName, {
|
||||||
|
|
@ -15141,7 +15151,9 @@ describe('Team agent launch matrix safe e2e', () => {
|
||||||
|
|
||||||
await (svc as any).launchMixedSecondaryLaneIfNeeded(stoppedRun);
|
await (svc as any).launchMixedSecondaryLaneIfNeeded(stoppedRun);
|
||||||
await (svc as any).launchMixedSecondaryLaneIfNeeded(survivingRun);
|
await (svc as any).launchMixedSecondaryLaneIfNeeded(survivingRun);
|
||||||
await waitForCondition(() => adapter.pendingLaunchInputs.length === 1);
|
await waitForCondition(() =>
|
||||||
|
adapter.pendingLaunchInputs.some((input) => input.teamName === stoppedTeamName)
|
||||||
|
);
|
||||||
|
|
||||||
svc.stopTeam(stoppedTeamName);
|
svc.stopTeam(stoppedTeamName);
|
||||||
|
|
||||||
|
|
@ -15864,7 +15876,9 @@ describe('Team agent launch matrix safe e2e', () => {
|
||||||
|
|
||||||
await (svc as any).launchMixedSecondaryLaneIfNeeded(cancelledRun);
|
await (svc as any).launchMixedSecondaryLaneIfNeeded(cancelledRun);
|
||||||
await (svc as any).launchMixedSecondaryLaneIfNeeded(survivingRun);
|
await (svc as any).launchMixedSecondaryLaneIfNeeded(survivingRun);
|
||||||
await waitForCondition(() => adapter.pendingLaunchInputs.length === 1);
|
await waitForCondition(() =>
|
||||||
|
adapter.pendingLaunchInputs.some((input) => input.teamName === cancelledTeamName)
|
||||||
|
);
|
||||||
|
|
||||||
await svc.cancelProvisioning(cancelledRun.runId);
|
await svc.cancelProvisioning(cancelledRun.runId);
|
||||||
|
|
||||||
|
|
@ -17108,7 +17122,7 @@ async function upsertActiveOpenCodeRuntimeLaneForTest(input: {
|
||||||
|
|
||||||
async function waitForCondition(assertion: () => boolean | Promise<boolean>): Promise<void> {
|
async function waitForCondition(assertion: () => boolean | Promise<boolean>): Promise<void> {
|
||||||
const startedAt = Date.now();
|
const startedAt = Date.now();
|
||||||
while (Date.now() - startedAt < 5_000) {
|
while (Date.now() - startedAt < 20_000) {
|
||||||
if (await assertion()) {
|
if (await assertion()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,4 +176,31 @@ describe('TeamInboxReader', () => {
|
||||||
summary: 'Comment on #abcd1234',
|
summary: 'Comment on #abcd1234',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('preserves agent error semantic kind from the team lead inbox', async () => {
|
||||||
|
hoisted.files.set(
|
||||||
|
'/mock/teams/my-team/inboxes/team-lead.json',
|
||||||
|
JSON.stringify([
|
||||||
|
{
|
||||||
|
from: 'bob',
|
||||||
|
to: 'team-lead',
|
||||||
|
text: 'bob hit a mailbox turn execution error. API Error: Credit balance is too low',
|
||||||
|
timestamp: '2026-01-01T03:00:00.000Z',
|
||||||
|
read: false,
|
||||||
|
messageId: 'm-agent-error',
|
||||||
|
messageKind: 'agent_error',
|
||||||
|
summary: 'Mailbox turn execution failed',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
const messages = await reader.getMessagesFor('my-team', 'team-lead');
|
||||||
|
expect(messages).toHaveLength(1);
|
||||||
|
expect(messages[0]).toMatchObject({
|
||||||
|
messageId: 'm-agent-error',
|
||||||
|
to: 'team-lead',
|
||||||
|
messageKind: 'agent_error',
|
||||||
|
summary: 'Mailbox turn execution failed',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@ vi.mock('agent-teams-controller', () => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { buildLegacyInboxMessageId } from '../../../../src/main/services/team/inboxMessageIdentity';
|
import { buildLegacyInboxMessageId } from '../../../../src/main/services/team/inboxMessageIdentity';
|
||||||
|
import { TeamConfigReader } from '../../../../src/main/services/team/TeamConfigReader';
|
||||||
import { TeamProvisioningService } from '../../../../src/main/services/team/TeamProvisioningService';
|
import { TeamProvisioningService } from '../../../../src/main/services/team/TeamProvisioningService';
|
||||||
|
|
||||||
function seedConfig(teamName: string): void {
|
function seedConfig(teamName: string): void {
|
||||||
|
|
@ -249,6 +250,7 @@ async function waitForCapture(service: TeamProvisioningService): Promise<any> {
|
||||||
|
|
||||||
describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
TeamConfigReader.clearCacheForTests();
|
||||||
hoisted.files.clear();
|
hoisted.files.clear();
|
||||||
hoisted.readFile.mockClear();
|
hoisted.readFile.mockClear();
|
||||||
hoisted.mkdir.mockClear();
|
hoisted.mkdir.mockClear();
|
||||||
|
|
|
||||||
|
|
@ -347,6 +347,39 @@ describe('ActivityItem slash command rendering', () => {
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders agent error messages with the dedicated Agent Error badge', async () => {
|
||||||
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
|
const host = document.createElement('div');
|
||||||
|
document.body.appendChild(host);
|
||||||
|
const root = createRoot(host);
|
||||||
|
|
||||||
|
const message: InboxMessage = {
|
||||||
|
from: 'bob',
|
||||||
|
to: 'team-lead',
|
||||||
|
text: 'bob hit a mailbox turn execution error for #abc12345. API Error: Credit balance is too low',
|
||||||
|
timestamp: new Date('2026-05-01T12:02:00.000Z').toISOString(),
|
||||||
|
read: false,
|
||||||
|
source: 'inbox',
|
||||||
|
messageKind: 'agent_error',
|
||||||
|
summary: 'Mailbox turn execution failed',
|
||||||
|
};
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
root.render(React.createElement(ActivityItem, { message, teamName: 'my-team' }));
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const badgeTexts = Array.from(host.querySelectorAll('span')).map((node) =>
|
||||||
|
node.textContent?.trim()
|
||||||
|
);
|
||||||
|
expect(badgeTexts).toContain('Agent Error');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
root.unmount();
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ActivityItem legacy system message fallback', () => {
|
describe('ActivityItem legacy system message fallback', () => {
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ describe('MemberExecutionLog', () => {
|
||||||
await flushMicrotasks();
|
await flushMicrotasks();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(host.textContent).toContain('jack turn');
|
expect(host.textContent ?? '').toMatch(/jack\s*turn/);
|
||||||
expect(host.textContent).not.toContain('Agent turn');
|
expect(host.textContent).not.toContain('Agent turn');
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
|
|
||||||
|
|
@ -414,7 +414,7 @@ describe('TaskLogStreamSection integration', () => {
|
||||||
expect(text).toContain('Task Log Stream');
|
expect(text).toContain('Task Log Stream');
|
||||||
expect(text).toContain('Grep');
|
expect(text).toContain('Grep');
|
||||||
expect(text).toContain('Edit');
|
expect(text).toContain('Edit');
|
||||||
expect(text).toContain('tom turn');
|
expect(text).toMatch(/tom\s*turn/);
|
||||||
expect(text).toContain('3 tool calls');
|
expect(text).toContain('3 tool calls');
|
||||||
expect(text).not.toContain('[]');
|
expect(text).not.toContain('[]');
|
||||||
expect(text).not.toContain('Audit complete');
|
expect(text).not.toContain('Audit complete');
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ describe('TaskLogStreamSection OpenCode real fixture e2e', () => {
|
||||||
const text = host.textContent ?? '';
|
const text = host.textContent ?? '';
|
||||||
expect(text).toContain('Task Log Stream');
|
expect(text).toContain('Task Log Stream');
|
||||||
expect(text).toContain('matched task tool markers');
|
expect(text).toContain('matched task tool markers');
|
||||||
expect(text).toContain('jack turn');
|
expect(text).toMatch(/jack\s*turn/);
|
||||||
expect(text).toContain('Calculator behavior');
|
expect(text).toContain('Calculator behavior');
|
||||||
expect(text).toContain('Задача #0b3a0624 завершена');
|
expect(text).toContain('Задача #0b3a0624 завершена');
|
||||||
expect(text).not.toContain('Keyboard handlers added');
|
expect(text).not.toContain('Keyboard handlers added');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue