Skip to content

Commit 8690c88

Browse files
committed
fix: recreate agent on token refresh
1 parent 2789312 commit 8690c88

3 files changed

Lines changed: 28 additions & 6 deletions

File tree

apps/array/src/main/services/agent/service.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ interface ManagedSession {
144144
lastActivityAt: number;
145145
mockNodeDir: string;
146146
config: SessionConfig;
147+
needsRecreation: boolean;
147148
}
148149

149150
function getClaudeCliPath(): string {
@@ -168,7 +169,17 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
168169

169170
public updateToken(newToken: string): void {
170171
this.currentToken = newToken;
171-
log.info("Session token updated");
172+
173+
// Mark all sessions for recreation - they'll be recreated before the next prompt.
174+
// We don't recreate immediately because the subprocess may be mid-response or
175+
// waiting on a permission prompt. Recreation happens at a safe point.
176+
for (const session of this.sessions.values()) {
177+
session.needsRecreation = true;
178+
}
179+
180+
log.info("Token updated, marked sessions for recreation", {
181+
sessionCount: this.sessions.size,
182+
});
172183
}
173184

174185
/**
@@ -335,6 +346,7 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
335346
const { clientStreams } = await agent.runTaskV2(taskId, taskRunId, {
336347
skipGitBranch: true,
337348
framework,
349+
isReconnect,
338350
});
339351

340352
const connection = this.createClientConnection(
@@ -385,6 +397,7 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
385397
lastActivityAt: Date.now(),
386398
mockNodeDir,
387399
config,
400+
needsRecreation: false,
388401
};
389402

390403
this.sessions.set(taskRunId, session);
@@ -416,7 +429,7 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
416429
throw new Error(`Session not found for recreation: ${taskRunId}`);
417430
}
418431

419-
log.info("Recreating session due to auth error", { taskRunId });
432+
log.info("Recreating session", { taskRunId });
420433

421434
const config = existing.config;
422435
this.cleanupSession(taskRunId);
@@ -438,6 +451,12 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
438451
throw new Error(`Session not found: ${sessionId}`);
439452
}
440453

454+
// Recreate session if marked (token was refreshed while session was active)
455+
if (session.needsRecreation) {
456+
log.info("Recreating session before prompt (token refreshed)", { sessionId });
457+
session = await this.recreateSession(sessionId);
458+
}
459+
441460
session.lastActivityAt = Date.now();
442461

443462
try {

packages/agent/src/agent.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,12 @@ export class Agent {
189189
);
190190
};
191191

192-
await sendNotification(POSTHOG_NOTIFICATIONS.RUN_STARTED, {
193-
sessionId: taskRunId,
194-
runId: taskRunId,
195-
});
192+
if (!options.isReconnect) {
193+
await sendNotification(POSTHOG_NOTIFICATIONS.RUN_STARTED, {
194+
sessionId: taskRunId,
195+
runId: taskRunId,
196+
});
197+
}
196198

197199
// Only fetch task when we need the slug for git branch creation
198200
if (!options.skipGitBranch) {

packages/agent/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ export interface TaskExecutionOptions {
143143
skipGitBranch?: boolean; // Skip creating a task-specific git branch
144144
framework?: "claude" | "codex"; // Agent framework to use (defaults to "claude")
145145
task?: Task; // Pre-fetched task to avoid redundant API call
146+
isReconnect?: boolean; // Session recreation - skip RUN_STARTED notification
146147
}
147148

148149
export interface ExecutionResult {

0 commit comments

Comments
 (0)