fix kiro fresh auto-run state reset

This commit is contained in:
QLHazyCoder
2026-05-22 07:57:36 +08:00
parent 5610262f7d
commit 25c6b8b4a7
6 changed files with 308 additions and 5 deletions
+172
View File
@@ -198,6 +198,8 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
assert.equal(currentState.flowId, 'kiro');
assert.equal(currentState.kiroTargetId, 'kiro-rs');
assert.equal(currentState.customFutureFlowField, 'future-ready');
assert.equal(currentState.nodeStatuses?.['kiro-open-register-page'], 'pending');
assert.equal(currentState.nodeStatuses?.['kiro-submit-email'], 'pending');
currentState = {
...currentState,
nodeStatuses: {
@@ -245,6 +247,176 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
assert.equal(helperCalls, 1);
});
test('auto-run controller clears stale kiro completed statuses before a fresh restart', async () => {
const source = fs.readFileSync('background/auto-run-controller.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundAutoRunController;`)(globalScope);
const kiroNodeIds = [
'kiro-open-register-page',
'kiro-submit-email',
'kiro-submit-name',
];
let currentState = {
activeFlowId: 'kiro',
flowId: 'kiro',
nodeStatuses: {
'kiro-open-register-page': 'completed',
'kiro-submit-email': 'pending',
'kiro-submit-name': 'pending',
},
runtimeState: {
activeFlowId: 'kiro',
currentNodeId: 'kiro-submit-email',
nodeStatuses: {
'kiro-open-register-page': 'completed',
},
flowState: {
kiro: {
register: {
loginUrl: '',
},
},
},
},
autoRunRoundSummaries: [],
autoRunSkipFailures: false,
};
const executedNodeIds = [];
let resetCalls = 0;
let sessionSeed = 1200;
const runtime = {
state: {
autoRunActive: false,
autoRunCurrentRun: 0,
autoRunTotalRuns: 1,
autoRunAttemptRun: 0,
autoRunSessionId: 0,
},
get() {
return { ...this.state };
},
set(updates = {}) {
this.state = { ...this.state, ...updates };
},
};
const controller = api.createAutoRunController({
addLog: async () => {},
appendAccountRunRecord: async () => null,
AUTO_RUN_MAX_RETRIES_PER_ROUND: 3,
AUTO_RUN_RETRY_DELAY_MS: 3000,
AUTO_RUN_TIMER_KIND_BEFORE_RETRY: 'before_retry',
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS: 'between_rounds',
broadcastAutoRunStatus: async (_phase, payload = {}, extraState = {}) => {
currentState = { ...currentState, ...extraState, ...payload };
},
broadcastStopToContentScripts: async () => {},
buildFreshAutoRunKeepState: (prevState = {}) => ({
activeFlowId: prevState.activeFlowId,
flowId: prevState.flowId,
runtimeState: {
activeFlowId: 'kiro',
currentNodeId: 'kiro-submit-email',
nodeStatuses: {
'kiro-open-register-page': 'completed',
},
flowState: prevState.runtimeState?.flowState || {},
},
}),
cancelPendingCommands: () => {},
clearStopRequest: () => {},
createAutoRunSessionId: () => {
sessionSeed += 1;
return sessionSeed;
},
ensureHotmailMailboxReadyForAutoRunRound: async () => {},
getAutoRunStatusPayload: (phase, payload = {}) => ({
autoRunning: phase === 'running',
autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
autoRunAttemptRun: payload.attemptRun ?? 0,
autoRunSessionId: payload.sessionId ?? 0,
}),
getErrorMessage: (error) => error?.message || String(error || ''),
getFirstUnfinishedNodeId: (statuses = {}) => {
for (const nodeId of kiroNodeIds) {
const status = String(statuses?.[nodeId] || 'pending').trim();
if (!['completed', 'manual_completed', 'skipped'].includes(status)) {
return nodeId;
}
}
return '';
},
getPendingAutoRunTimerPlan: () => null,
getRunningNodeIds: () => [],
getState: async () => ({
...currentState,
nodeStatuses: { ...(currentState.nodeStatuses || {}) },
runtimeState: currentState.runtimeState ? JSON.parse(JSON.stringify(currentState.runtimeState)) : undefined,
}),
getStopRequested: () => false,
hasSavedNodeProgress: () => false,
isAddPhoneAuthFailure: () => false,
isGpcTaskEndedFailure: () => false,
isKiroProxyFailure: () => false,
isPhoneSmsPlatformRateLimitFailure: () => false,
isPlusCheckoutNonFreeTrialFailure: () => false,
isRestartCurrentAttemptError: () => false,
isStep4Route405RecoveryLimitFailure: () => false,
isSignupUserAlreadyExistsFailure: () => false,
isStopError: () => false,
launchAutoRunTimerPlan: async () => false,
normalizeAutoRunFallbackThreadIntervalMinutes: () => 0,
persistAutoRunTimerPlan: async () => {},
resetState: async () => {
resetCalls += 1;
currentState = {
activeFlowId: 'kiro',
flowId: 'kiro',
nodeStatuses: {},
runtimeState: {
activeFlowId: 'kiro',
flowState: {},
},
};
},
runAutoSequenceFromNode: async (nodeId) => {
executedNodeIds.push(nodeId);
assert.equal(nodeId, 'kiro-open-register-page');
assert.equal(currentState.currentNodeId, '');
assert.equal(currentState.nodeStatuses['kiro-open-register-page'], 'pending');
assert.equal(currentState.nodeStatuses['kiro-submit-email'], 'pending');
assert.equal(currentState.runtimeState.currentNodeId, undefined);
assert.equal(currentState.runtimeState.nodeStatuses, undefined);
currentState.nodeStatuses = Object.fromEntries(kiroNodeIds.map((id) => [id, 'completed']));
},
runtime,
setState: async (updates = {}) => {
currentState = {
...currentState,
...updates,
nodeStatuses: updates.nodeStatuses ? { ...updates.nodeStatuses } : currentState.nodeStatuses,
runtimeState: updates.runtimeState ? JSON.parse(JSON.stringify(updates.runtimeState)) : currentState.runtimeState,
};
},
sleepWithStop: async () => {},
throwIfAutoRunSessionStopped: () => {},
waitForRunningNodesToFinish: async () => currentState,
chrome: {
runtime: {
sendMessage: () => Promise.resolve(),
},
},
});
await controller.autoRunLoop(1, { autoRunSkipFailures: false, mode: 'restart' });
assert.equal(resetCalls, 1);
assert.deepEqual(executedNodeIds, ['kiro-open-register-page']);
});
test('auto-run controller stops immediately on kiro proxy failures even when skip-failures is enabled', async () => {
const source = fs.readFileSync('background/auto-run-controller.js', 'utf8');
const globalScope = {};
@@ -88,6 +88,7 @@ test('kiro submit-email stops immediately when AWS routes the email to login', a
addLog: async () => {},
chrome: {
tabs: {
get: async (tabId) => ({ id: tabId, url: 'https://us-east-1.signin.aws/platform/d/signup' }),
update: async () => {},
},
},
@@ -152,6 +153,7 @@ test('kiro submit-email can adopt an already-open registration OTP page without
addLog: async () => {},
chrome: {
tabs: {
get: async (tabId) => ({ id: tabId, url: 'https://us-east-1.signin.aws/platform/d/signup' }),
update: async () => {},
},
},
@@ -180,3 +182,71 @@ test('kiro submit-email can adopt an already-open registration OTP page without
assert.equal(getKiroRuntime(completedPayload).register?.verificationRequestedAt, 0);
assert.equal(sentMessages.some((message) => message.type === 'EXECUTE_NODE'), false);
});
test('kiro submit-email reuses the step 1 register tab even when the source registry was reset', async () => {
const api = loadRegisterRunnerApi();
const currentState = {
runtimeState: {
flowState: {
kiro: {
session: {
registerTabId: 1770749825,
},
register: {
loginUrl: 'https://app.kiro.dev/signin',
},
},
},
},
};
const events = [];
const runner = api.createKiroRegisterRunner({
addLog: async () => {},
chrome: {
tabs: {
get: async (tabId) => {
events.push({ type: 'get', tabId });
return {
id: tabId,
url: 'https://us-east-1.signin.aws/platform/d-9067642ac7/signup',
};
},
update: async (tabId, payload) => {
events.push({ type: 'update', tabId, payload });
},
},
},
completeNodeFromBackground: async () => {},
getState: async () => currentState,
getTabId: async () => null,
isTabAlive: async () => false,
registerTab: async (source, tabId) => {
events.push({ type: 'register', source, tabId });
},
resolveSignupEmailForFlow: async () => 'fresh-user@duck.com',
reuseOrCreateTab: async () => {
events.push({ type: 'reuse-or-create' });
return 1770749826;
},
sendToContentScriptResilient: async (_sourceId, message) => {
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
return {
state: 'register_otp_page',
url: 'https://us-east-1.signin.aws/platform/d-9067642ac7/signup',
email: 'fresh-user@duck.com',
};
}
return {};
},
setState: async () => {},
});
await runner.executeKiroSubmitEmail({ nodeId: 'kiro-submit-email', ...currentState });
assert.equal(events.some((event) => event.type === 'reuse-or-create'), false);
assert.deepEqual(
events.filter((event) => event.type === 'register'),
[{ type: 'register', source: 'kiro-register-page', tabId: 1770749825 }]
);
assert.ok(events.some((event) => event.type === 'update' && event.tabId === 1770749825));
});
@@ -162,6 +162,8 @@ test('kiro state reset helpers clear downstream runtime and fresh keep-state pre
const keepState = api.buildFreshKeepState(currentState);
assert.equal(keepState.targetId, 'kiro-rs');
assert.equal(Object.prototype.hasOwnProperty.call(keepState, 'kiroRuntime'), false);
assert.equal(Object.prototype.hasOwnProperty.call(keepState.runtimeState, 'nodeStatuses'), false);
assert.equal(Object.prototype.hasOwnProperty.call(keepState.runtimeState, 'currentNodeId'), false);
assert.equal(getKiroRuntime(keepState).register.email, '');
assert.equal(getKiroRuntime(keepState).desktopAuth.refreshToken, '');
assert.equal(getKiroRuntime(keepState).upload.status, '');