fix(team): preserve launch error and permission details
This commit is contained in:
parent
65fa117626
commit
3e53391a73
5 changed files with 75 additions and 0 deletions
|
|
@ -63,6 +63,7 @@ export interface OpenCodeLaunchTeamCommandBody {
|
||||||
export interface OpenCodeTeamMemberLaunchCommandData {
|
export interface OpenCodeTeamMemberLaunchCommandData {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
launchState: OpenCodeTeamMemberLaunchBridgeState;
|
launchState: OpenCodeTeamMemberLaunchBridgeState;
|
||||||
|
pendingPermissionRequestIds?: string[];
|
||||||
model: string;
|
model: string;
|
||||||
runtimePid?: number;
|
runtimePid?: number;
|
||||||
evidence: Array<{ kind: string; observedAt: string }>;
|
evidence: Array<{ kind: string; observedAt: string }>;
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,7 @@ function mapOpenCodeLaunchDataToRuntimeResult(
|
||||||
fallbackLaunchState,
|
fallbackLaunchState,
|
||||||
bridgeMember?.sessionId,
|
bridgeMember?.sessionId,
|
||||||
bridgeMember?.runtimePid,
|
bridgeMember?.runtimePid,
|
||||||
|
bridgeMember?.pendingPermissionRequestIds,
|
||||||
bridgeMember != null,
|
bridgeMember != null,
|
||||||
[
|
[
|
||||||
...(bridgeMember
|
...(bridgeMember
|
||||||
|
|
@ -426,6 +427,7 @@ function mapBridgeMemberToRuntimeEvidence(
|
||||||
launchState: OpenCodeTeamMemberLaunchBridgeState,
|
launchState: OpenCodeTeamMemberLaunchBridgeState,
|
||||||
sessionId: string | undefined,
|
sessionId: string | undefined,
|
||||||
runtimePid: number | undefined,
|
runtimePid: number | undefined,
|
||||||
|
pendingPermissionRequestIds: string[] | undefined,
|
||||||
runtimeMaterialized: boolean,
|
runtimeMaterialized: boolean,
|
||||||
diagnostics: string[]
|
diagnostics: string[]
|
||||||
): TeamRuntimeMemberLaunchEvidence {
|
): TeamRuntimeMemberLaunchEvidence {
|
||||||
|
|
@ -448,6 +450,10 @@ function mapBridgeMemberToRuntimeEvidence(
|
||||||
bootstrapConfirmed: confirmed,
|
bootstrapConfirmed: confirmed,
|
||||||
hardFailure: failed,
|
hardFailure: failed,
|
||||||
hardFailureReason: failed ? 'OpenCode bridge reported member launch failure' : undefined,
|
hardFailureReason: failed ? 'OpenCode bridge reported member launch failure' : undefined,
|
||||||
|
pendingPermissionRequestIds:
|
||||||
|
pendingPermissionRequestIds && pendingPermissionRequestIds.length > 0
|
||||||
|
? [...new Set(pendingPermissionRequestIds)]
|
||||||
|
: undefined,
|
||||||
sessionId,
|
sessionId,
|
||||||
...(typeof runtimePid === 'number' && Number.isFinite(runtimePid) && runtimePid > 0
|
...(typeof runtimePid === 'number' && Number.isFinite(runtimePid) && runtimePid > 0
|
||||||
? { runtimePid }
|
? { runtimePid }
|
||||||
|
|
|
||||||
|
|
@ -733,6 +733,7 @@ function areMemberSpawnStatusEntriesEqual(
|
||||||
left.status === right.status &&
|
left.status === right.status &&
|
||||||
left.launchState === right.launchState &&
|
left.launchState === right.launchState &&
|
||||||
left.error === right.error &&
|
left.error === right.error &&
|
||||||
|
left.hardFailureReason === right.hardFailureReason &&
|
||||||
left.livenessSource === right.livenessSource &&
|
left.livenessSource === right.livenessSource &&
|
||||||
left.runtimeAlive === right.runtimeAlive &&
|
left.runtimeAlive === right.runtimeAlive &&
|
||||||
left.runtimeModel === right.runtimeModel &&
|
left.runtimeModel === right.runtimeModel &&
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,7 @@ describe('OpenCodeTeamRuntimeAdapter', () => {
|
||||||
alice: {
|
alice: {
|
||||||
sessionId: 'oc-session-1',
|
sessionId: 'oc-session-1',
|
||||||
launchState: 'permission_blocked',
|
launchState: 'permission_blocked',
|
||||||
|
pendingPermissionRequestIds: ['perm-1', 'perm-1', 'perm-2'],
|
||||||
runtimePid: 123,
|
runtimePid: 123,
|
||||||
model: 'openai/gpt-5.4-mini',
|
model: 'openai/gpt-5.4-mini',
|
||||||
evidence: [
|
evidence: [
|
||||||
|
|
@ -283,6 +284,7 @@ describe('OpenCodeTeamRuntimeAdapter', () => {
|
||||||
alice: {
|
alice: {
|
||||||
providerId: 'opencode',
|
providerId: 'opencode',
|
||||||
launchState: 'runtime_pending_permission',
|
launchState: 'runtime_pending_permission',
|
||||||
|
pendingPermissionRequestIds: ['perm-1', 'perm-2'],
|
||||||
runtimeAlive: true,
|
runtimeAlive: true,
|
||||||
agentToolAccepted: true,
|
agentToolAccepted: true,
|
||||||
bootstrapConfirmed: false,
|
bootstrapConfirmed: false,
|
||||||
|
|
|
||||||
|
|
@ -3463,6 +3463,71 @@ describe('teamSlice actions', () => {
|
||||||
expect(store.getState().memberSpawnSnapshotsByTeam['my-team']).toEqual(nextSnapshot);
|
expect(store.getState().memberSpawnSnapshotsByTeam['my-team']).toEqual(nextSnapshot);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('rewrites renderer state when only hard failure reason changes', async () => {
|
||||||
|
const store = createSliceStore();
|
||||||
|
const previousSnapshot = createMemberSpawnSnapshot({
|
||||||
|
teamLaunchState: 'partial_failure',
|
||||||
|
summary: {
|
||||||
|
confirmedCount: 0,
|
||||||
|
pendingCount: 0,
|
||||||
|
failedCount: 1,
|
||||||
|
runtimeAlivePendingCount: 0,
|
||||||
|
},
|
||||||
|
statuses: {
|
||||||
|
alice: createMemberSpawnStatus({
|
||||||
|
status: 'error',
|
||||||
|
launchState: 'failed_to_start',
|
||||||
|
runtimeAlive: false,
|
||||||
|
livenessSource: undefined,
|
||||||
|
bootstrapConfirmed: false,
|
||||||
|
hardFailure: true,
|
||||||
|
hardFailureReason: 'initial failure',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const previousStatuses = previousSnapshot.statuses;
|
||||||
|
|
||||||
|
store.setState({
|
||||||
|
currentRuntimeRunIdByTeam: {
|
||||||
|
'my-team': 'runtime-run',
|
||||||
|
},
|
||||||
|
memberSpawnStatusesByTeam: {
|
||||||
|
'my-team': previousStatuses,
|
||||||
|
},
|
||||||
|
memberSpawnSnapshotsByTeam: {
|
||||||
|
'my-team': previousSnapshot,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const nextSnapshot = createMemberSpawnSnapshot({
|
||||||
|
teamLaunchState: 'partial_failure',
|
||||||
|
summary: {
|
||||||
|
confirmedCount: 0,
|
||||||
|
pendingCount: 0,
|
||||||
|
failedCount: 1,
|
||||||
|
runtimeAlivePendingCount: 0,
|
||||||
|
},
|
||||||
|
statuses: {
|
||||||
|
alice: createMemberSpawnStatus({
|
||||||
|
status: 'error',
|
||||||
|
launchState: 'failed_to_start',
|
||||||
|
runtimeAlive: false,
|
||||||
|
livenessSource: undefined,
|
||||||
|
bootstrapConfirmed: false,
|
||||||
|
hardFailure: true,
|
||||||
|
hardFailureReason: 'resolved runtime reported missing auth',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
hoisted.getMemberSpawnStatuses.mockResolvedValue(nextSnapshot);
|
||||||
|
|
||||||
|
await store.getState().fetchMemberSpawnStatuses('my-team');
|
||||||
|
|
||||||
|
expect(store.getState().memberSpawnStatusesByTeam['my-team']).not.toBe(previousStatuses);
|
||||||
|
expect(store.getState().memberSpawnStatusesByTeam['my-team']).toEqual(nextSnapshot.statuses);
|
||||||
|
expect(store.getState().memberSpawnSnapshotsByTeam['my-team']).toEqual(nextSnapshot);
|
||||||
|
});
|
||||||
|
|
||||||
it('rewrites renderer state when top-level launch summary changes', async () => {
|
it('rewrites renderer state when top-level launch summary changes', async () => {
|
||||||
const store = createSliceStore();
|
const store = createSliceStore();
|
||||||
const previousSnapshot = createMemberSpawnSnapshot({
|
const previousSnapshot = createMemberSpawnSnapshot({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue