mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 07:04:25 +00:00
refactor(hooks): split session events into individual typed events
Major changes: - Replace monolithic SessionEvent with reason discriminator with individual event types: session_start, session_before_switch, session_switch, session_before_new, session_new, session_before_branch, session_branch, session_before_compact, session_compact, session_shutdown - Each event has dedicated result type (SessionBeforeSwitchResult, etc.) - HookHandler type now allows bare return statements (void in return type) - HookAPI.on() has proper overloads for each event with correct typing Additional fixes: - AgentSession now always subscribes to agent in constructor (was only subscribing when external subscribe() called, breaking internal handlers) - Standardize on undefined over null throughout codebase - HookUIContext methods return undefined instead of null - SessionManager methods return undefined instead of null - Simplify hook exports to 'export type * from types.js' - Add detailed JSDoc for skipConversationRestore vs cancel - Fix createBranchedSession to rebuild index in persist mode - newSession() now returns the session file path Updated all example hooks, tests, and emission sites to use new event types.
This commit is contained in:
parent
38d65dfe59
commit
d6283f99dc
43 changed files with 2129 additions and 640 deletions
|
|
@ -450,7 +450,7 @@ export class SessionManager {
|
|||
private labelsById: Map<string, string> = new Map();
|
||||
private leafId: string = "";
|
||||
|
||||
private constructor(cwd: string, sessionDir: string, sessionFile: string | null, persist: boolean) {
|
||||
private constructor(cwd: string, sessionDir: string, sessionFile: string | undefined, persist: boolean) {
|
||||
this.cwd = cwd;
|
||||
this.sessionDir = sessionDir;
|
||||
this.persist = persist;
|
||||
|
|
@ -484,7 +484,7 @@ export class SessionManager {
|
|||
}
|
||||
}
|
||||
|
||||
newSession(): void {
|
||||
newSession(): string | undefined {
|
||||
this.sessionId = randomUUID();
|
||||
const timestamp = new Date().toISOString();
|
||||
const header: SessionHeader = {
|
||||
|
|
@ -503,6 +503,7 @@ export class SessionManager {
|
|||
const fileTimestamp = timestamp.replace(/[:.]/g, "-");
|
||||
this.sessionFile = join(this.getSessionDir(), `${fileTimestamp}_${this.sessionId}.jsonl`);
|
||||
}
|
||||
return this.sessionFile;
|
||||
}
|
||||
|
||||
private _buildIndex(): void {
|
||||
|
|
@ -841,9 +842,9 @@ export class SessionManager {
|
|||
/**
|
||||
* Create a new session file containing only the path from root to the specified leaf.
|
||||
* Useful for extracting a single conversation path from a branched session.
|
||||
* Returns the new session file path, or null if not persisting.
|
||||
* Returns the new session file path, or undefined if not persisting.
|
||||
*/
|
||||
createBranchedSession(leafId: string): string | null {
|
||||
createBranchedSession(leafId: string): string | undefined {
|
||||
const path = this.getPath(leafId);
|
||||
if (path.length === 0) {
|
||||
throw new Error(`Entry ${leafId} not found`);
|
||||
|
|
@ -883,6 +884,7 @@ export class SessionManager {
|
|||
// Write fresh label entries at the end
|
||||
const lastEntryId = pathWithoutLabels[pathWithoutLabels.length - 1]?.id || null;
|
||||
let parentId = lastEntryId;
|
||||
const labelEntries: LabelEntry[] = [];
|
||||
for (const { targetId, label } of labelsToWrite) {
|
||||
const labelEntry: LabelEntry = {
|
||||
type: "label",
|
||||
|
|
@ -894,8 +896,12 @@ export class SessionManager {
|
|||
};
|
||||
appendFileSync(newSessionFile, `${JSON.stringify(labelEntry)}\n`);
|
||||
pathEntryIds.add(labelEntry.id);
|
||||
labelEntries.push(labelEntry);
|
||||
parentId = labelEntry.id;
|
||||
}
|
||||
this.fileEntries = [header, ...pathWithoutLabels, ...labelEntries];
|
||||
this.sessionId = newSessionId;
|
||||
this._buildIndex();
|
||||
return newSessionFile;
|
||||
}
|
||||
|
||||
|
|
@ -917,7 +923,7 @@ export class SessionManager {
|
|||
this.fileEntries = [header, ...pathWithoutLabels, ...labelEntries];
|
||||
this.sessionId = newSessionId;
|
||||
this._buildIndex();
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -927,7 +933,7 @@ export class SessionManager {
|
|||
*/
|
||||
static create(cwd: string, sessionDir?: string): SessionManager {
|
||||
const dir = sessionDir ?? getDefaultSessionDir(cwd);
|
||||
return new SessionManager(cwd, dir, null, true);
|
||||
return new SessionManager(cwd, dir, undefined, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -956,12 +962,12 @@ export class SessionManager {
|
|||
if (mostRecent) {
|
||||
return new SessionManager(cwd, dir, mostRecent, true);
|
||||
}
|
||||
return new SessionManager(cwd, dir, null, true);
|
||||
return new SessionManager(cwd, dir, undefined, true);
|
||||
}
|
||||
|
||||
/** Create an in-memory session (no file persistence) */
|
||||
static inMemory(cwd: string = process.cwd()): SessionManager {
|
||||
return new SessionManager(cwd, "", null, false);
|
||||
return new SessionManager(cwd, "", undefined, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue