feat: defer session creation until first user+assistant message exchange

- Sessions are no longer created immediately on startup
- Session files only created after at least 1 user message and 1 assistant response
- Prevents empty session files when agent is launched and immediately quit
- Messages are queued until session is initialized
- Continue/resume modes properly mark sessions as already initialized
This commit is contained in:
Mario Zechner 2025-11-12 22:06:02 +01:00
parent 5e988b444b
commit 812f2f43cd
2 changed files with 55 additions and 6 deletions

View file

@ -425,8 +425,8 @@ export async function main(args: string[]) {
}
}
// Start session
sessionManager.startSession(agent.state);
// Note: Session will be started lazily after first user+assistant message exchange
// (unless continuing/resuming, in which case it's already initialized)
// Inject project context (AGENT.md/CLAUDE.md) if not continuing/resuming
if (!parsed.continue && !parsed.resume) {
@ -454,6 +454,11 @@ export async function main(args: string[]) {
// Save messages on completion
if (event.type === "message_end") {
sessionManager.saveMessage(event.message);
// Check if we should initialize session now (after first user+assistant exchange)
if (sessionManager.shouldInitializeSession(agent.state.messages)) {
sessionManager.startSession(agent.state);
}
}
});

View file

@ -44,6 +44,8 @@ export class SessionManager {
private sessionFile!: string;
private sessionDir: string;
private enabled: boolean = true;
private sessionInitialized: boolean = false;
private pendingMessages: any[] = [];
constructor(continueSession: boolean = false, customSessionPath?: string) {
this.sessionDir = this.getSessionDirectory();
@ -52,11 +54,15 @@ export class SessionManager {
// Use custom session file path
this.sessionFile = resolve(customSessionPath);
this.loadSessionId();
// Mark as initialized since we're loading an existing session
this.sessionInitialized = existsSync(this.sessionFile);
} else if (continueSession) {
const mostRecent = this.findMostRecentlyModifiedSession();
if (mostRecent) {
this.sessionFile = mostRecent;
this.loadSessionId();
// Mark as initialized since we're loading an existing session
this.sessionInitialized = true;
} else {
this.initNewSession();
}
@ -124,7 +130,9 @@ export class SessionManager {
}
startSession(state: AgentState): void {
if (!this.enabled) return;
if (!this.enabled || this.sessionInitialized) return;
this.sessionInitialized = true;
const entry: SessionHeader = {
type: "session",
id: this.sessionId,
@ -134,6 +142,12 @@ export class SessionManager {
thinkingLevel: state.thinkingLevel,
};
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
// Write any queued messages
for (const msg of this.pendingMessages) {
appendFileSync(this.sessionFile, JSON.stringify(msg) + "\n");
}
this.pendingMessages = [];
}
saveMessage(message: any): void {
@ -143,7 +157,12 @@ export class SessionManager {
timestamp: new Date().toISOString(),
message,
};
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
if (!this.sessionInitialized) {
this.pendingMessages.push(entry);
} else {
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
}
}
saveThinkingLevelChange(thinkingLevel: string): void {
@ -153,7 +172,12 @@ export class SessionManager {
timestamp: new Date().toISOString(),
thinkingLevel,
};
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
if (!this.sessionInitialized) {
this.pendingMessages.push(entry);
} else {
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
}
}
saveModelChange(model: string): void {
@ -163,7 +187,12 @@ export class SessionManager {
timestamp: new Date().toISOString(),
model,
};
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
if (!this.sessionInitialized) {
this.pendingMessages.push(entry);
} else {
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
}
}
loadMessages(): any[] {
@ -345,5 +374,20 @@ export class SessionManager {
setSessionFile(path: string): void {
this.sessionFile = path;
this.loadSessionId();
// Mark as initialized since we're loading an existing session
this.sessionInitialized = existsSync(path);
}
/**
* Check if we should initialize the session based on message history.
* Session is initialized when we have at least 1 user message and 1 assistant message.
*/
shouldInitializeSession(messages: any[]): boolean {
if (this.sessionInitialized) return false;
const userMessages = messages.filter((m) => m.role === "user");
const assistantMessages = messages.filter((m) => m.role === "assistant");
return userMessages.length >= 1 && assistantMessages.length >= 1;
}
}