mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-21 12:00:15 +00:00
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:
parent
5e988b444b
commit
812f2f43cd
2 changed files with 55 additions and 6 deletions
|
|
@ -425,8 +425,8 @@ export async function main(args: string[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start session
|
// Note: Session will be started lazily after first user+assistant message exchange
|
||||||
sessionManager.startSession(agent.state);
|
// (unless continuing/resuming, in which case it's already initialized)
|
||||||
|
|
||||||
// Inject project context (AGENT.md/CLAUDE.md) if not continuing/resuming
|
// Inject project context (AGENT.md/CLAUDE.md) if not continuing/resuming
|
||||||
if (!parsed.continue && !parsed.resume) {
|
if (!parsed.continue && !parsed.resume) {
|
||||||
|
|
@ -454,6 +454,11 @@ export async function main(args: string[]) {
|
||||||
// Save messages on completion
|
// Save messages on completion
|
||||||
if (event.type === "message_end") {
|
if (event.type === "message_end") {
|
||||||
sessionManager.saveMessage(event.message);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ export class SessionManager {
|
||||||
private sessionFile!: string;
|
private sessionFile!: string;
|
||||||
private sessionDir: string;
|
private sessionDir: string;
|
||||||
private enabled: boolean = true;
|
private enabled: boolean = true;
|
||||||
|
private sessionInitialized: boolean = false;
|
||||||
|
private pendingMessages: any[] = [];
|
||||||
|
|
||||||
constructor(continueSession: boolean = false, customSessionPath?: string) {
|
constructor(continueSession: boolean = false, customSessionPath?: string) {
|
||||||
this.sessionDir = this.getSessionDirectory();
|
this.sessionDir = this.getSessionDirectory();
|
||||||
|
|
@ -52,11 +54,15 @@ export class SessionManager {
|
||||||
// Use custom session file path
|
// Use custom session file path
|
||||||
this.sessionFile = resolve(customSessionPath);
|
this.sessionFile = resolve(customSessionPath);
|
||||||
this.loadSessionId();
|
this.loadSessionId();
|
||||||
|
// Mark as initialized since we're loading an existing session
|
||||||
|
this.sessionInitialized = existsSync(this.sessionFile);
|
||||||
} else if (continueSession) {
|
} else if (continueSession) {
|
||||||
const mostRecent = this.findMostRecentlyModifiedSession();
|
const mostRecent = this.findMostRecentlyModifiedSession();
|
||||||
if (mostRecent) {
|
if (mostRecent) {
|
||||||
this.sessionFile = mostRecent;
|
this.sessionFile = mostRecent;
|
||||||
this.loadSessionId();
|
this.loadSessionId();
|
||||||
|
// Mark as initialized since we're loading an existing session
|
||||||
|
this.sessionInitialized = true;
|
||||||
} else {
|
} else {
|
||||||
this.initNewSession();
|
this.initNewSession();
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +130,9 @@ export class SessionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
startSession(state: AgentState): void {
|
startSession(state: AgentState): void {
|
||||||
if (!this.enabled) return;
|
if (!this.enabled || this.sessionInitialized) return;
|
||||||
|
this.sessionInitialized = true;
|
||||||
|
|
||||||
const entry: SessionHeader = {
|
const entry: SessionHeader = {
|
||||||
type: "session",
|
type: "session",
|
||||||
id: this.sessionId,
|
id: this.sessionId,
|
||||||
|
|
@ -134,6 +142,12 @@ export class SessionManager {
|
||||||
thinkingLevel: state.thinkingLevel,
|
thinkingLevel: state.thinkingLevel,
|
||||||
};
|
};
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
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 {
|
saveMessage(message: any): void {
|
||||||
|
|
@ -143,8 +157,13 @@ export class SessionManager {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
message,
|
message,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!this.sessionInitialized) {
|
||||||
|
this.pendingMessages.push(entry);
|
||||||
|
} else {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
saveThinkingLevelChange(thinkingLevel: string): void {
|
saveThinkingLevelChange(thinkingLevel: string): void {
|
||||||
if (!this.enabled) return;
|
if (!this.enabled) return;
|
||||||
|
|
@ -153,8 +172,13 @@ export class SessionManager {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
thinkingLevel,
|
thinkingLevel,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!this.sessionInitialized) {
|
||||||
|
this.pendingMessages.push(entry);
|
||||||
|
} else {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
saveModelChange(model: string): void {
|
saveModelChange(model: string): void {
|
||||||
if (!this.enabled) return;
|
if (!this.enabled) return;
|
||||||
|
|
@ -163,8 +187,13 @@ export class SessionManager {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
model,
|
model,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!this.sessionInitialized) {
|
||||||
|
this.pendingMessages.push(entry);
|
||||||
|
} else {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loadMessages(): any[] {
|
loadMessages(): any[] {
|
||||||
if (!existsSync(this.sessionFile)) return [];
|
if (!existsSync(this.sessionFile)) return [];
|
||||||
|
|
@ -345,5 +374,20 @@ export class SessionManager {
|
||||||
setSessionFile(path: string): void {
|
setSessionFile(path: string): void {
|
||||||
this.sessionFile = path;
|
this.sessionFile = path;
|
||||||
this.loadSessionId();
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue