mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-19 18:04:41 +00:00
Make CompactionEntry and CompactionResult generic with details field
- CompactionEntry<T> and CompactionResult<T> now have optional details?: T - appendCompaction() accepts optional details parameter - Hooks can return compaction.details to store custom data - Enables structured compaction with ArtifactIndex (see #314) - Fix CompactionResult export location (now from compaction.ts) - Update plan with remaining compaction refactor items
This commit is contained in:
parent
efb1036d8e
commit
d96375b5e5
9 changed files with 54 additions and 24 deletions
|
|
@ -15,12 +15,15 @@
|
||||||
- New methods: `getTree()`, `getPath()`, `getLeafUuid()`, `getLeafEntry()`, `getEntry()`, `branchWithSummary()`
|
- New methods: `getTree()`, `getPath()`, `getLeafUuid()`, `getLeafEntry()`, `getEntry()`, `branchWithSummary()`
|
||||||
- New `appendCustomEntry(customType, data)` for hooks to store custom data
|
- New `appendCustomEntry(customType, data)` for hooks to store custom data
|
||||||
- **Compaction API**:
|
- **Compaction API**:
|
||||||
- `compact()` now returns `CompactionResult` (`{ summary, firstKeptEntryId, tokensBefore }`) instead of `CompactionEntry`
|
- `CompactionEntry<T>` and `CompactionResult<T>` are now generic with optional `details?: T` for hook-specific data
|
||||||
|
- `compact()` now returns `CompactionResult` (`{ summary, firstKeptEntryId, tokensBefore, details? }`) instead of `CompactionEntry`
|
||||||
|
- `appendCompaction()` now accepts optional `details` parameter
|
||||||
- `CompactionEntry.firstKeptEntryIndex` replaced with `firstKeptEntryId`
|
- `CompactionEntry.firstKeptEntryIndex` replaced with `firstKeptEntryId`
|
||||||
- `prepareCompaction()` now returns `firstKeptEntryId` in its result
|
- `prepareCompaction()` now returns `firstKeptEntryId` in its result
|
||||||
- **Hook types**:
|
- **Hook types**:
|
||||||
- `SessionEventResult.compactionEntry` replaced with `SessionEventResult.compaction` (content only, SessionManager adds id/parentId)
|
- `SessionEventResult.compactionEntry` replaced with `SessionEventResult.compaction` (content only, SessionManager adds id/parentId)
|
||||||
- `before_compact` event now includes `firstKeptEntryId` field for hooks that return custom compaction
|
- `before_compact` event now includes `firstKeptEntryId` field for hooks that return custom compaction
|
||||||
|
- Hooks can return `compaction.details` to store custom data (e.g., ArtifactIndex for structured compaction)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,18 @@ Reference: [session-tree.md](./session-tree.md)
|
||||||
|
|
||||||
### Compaction Refactor
|
### Compaction Refactor
|
||||||
|
|
||||||
- [ ] Clean up types passed to hooks (currently messy mix of `CompactionEntry`, `CompactionResult`, hook's `compaction` content)
|
- [x] Use `CompactionResult` type for hook return value
|
||||||
- [ ] Ensure consistent API between what hooks receive and what they return
|
- [ ] Make `CompactionEntry<T>` generic with optional `details?: T` field for hook-specific data
|
||||||
|
- [ ] Make `CompactionResult<T>` generic to match
|
||||||
|
- [ ] Update `SessionEventBase` to pass `sessionManager` and `modelRegistry` instead of derived fields
|
||||||
|
- [ ] Update `before_compact` event:
|
||||||
|
- Pass `preparation: CompactionPreparation` instead of individual fields
|
||||||
|
- Pass `previousCompactions: CompactionEntry[]` (newest first) instead of `previousSummary?: string`
|
||||||
|
- Keep: `customInstructions`, `model`, `signal`
|
||||||
|
- Drop: `resolveApiKey` (use `modelRegistry.getApiKey()`), `cutPoint`, `entries`
|
||||||
|
- [ ] Update hook example `custom-compaction.ts` to use new API
|
||||||
|
|
||||||
|
Reference: [#314](https://github.com/badlogic/pi-mono/pull/314) - Structured compaction with anchored iterative summarization needs `details` field to store `ArtifactIndex` and version markers.
|
||||||
|
|
||||||
### Branch Summary Design
|
### Branch Summary Design
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,13 @@ import type { AssistantMessage, Message, Model, TextContent } from "@mariozechne
|
||||||
import { isContextOverflow, modelsAreEqual, supportsXhigh } from "@mariozechner/pi-ai";
|
import { isContextOverflow, modelsAreEqual, supportsXhigh } from "@mariozechner/pi-ai";
|
||||||
import { getAuthPath } from "../config.js";
|
import { getAuthPath } from "../config.js";
|
||||||
import { type BashResult, executeBash as executeBashCommand } from "./bash-executor.js";
|
import { type BashResult, executeBash as executeBashCommand } from "./bash-executor.js";
|
||||||
import { calculateContextTokens, compact, prepareCompaction, shouldCompact } from "./compaction.js";
|
import {
|
||||||
|
type CompactionResult,
|
||||||
|
calculateContextTokens,
|
||||||
|
compact,
|
||||||
|
prepareCompaction,
|
||||||
|
shouldCompact,
|
||||||
|
} from "./compaction.js";
|
||||||
import type { LoadedCustomTool, SessionEvent as ToolSessionEvent } from "./custom-tools/index.js";
|
import type { LoadedCustomTool, SessionEvent as ToolSessionEvent } from "./custom-tools/index.js";
|
||||||
import { exportSessionToHtml } from "./export-html.js";
|
import { exportSessionToHtml } from "./export-html.js";
|
||||||
import type { HookRunner, SessionEventResult, TurnEndEvent, TurnStartEvent } from "./hooks/index.js";
|
import type { HookRunner, SessionEventResult, TurnEndEvent, TurnStartEvent } from "./hooks/index.js";
|
||||||
|
|
@ -76,12 +82,6 @@ export interface ModelCycleResult {
|
||||||
isScoped: boolean;
|
isScoped: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Result from compact() or checkAutoCompaction() */
|
|
||||||
export interface CompactionResult {
|
|
||||||
tokensBefore: number;
|
|
||||||
summary: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Session statistics for /session command */
|
/** Session statistics for /session command */
|
||||||
export interface SessionStats {
|
export interface SessionStats {
|
||||||
sessionFile: string | null;
|
sessionFile: string | null;
|
||||||
|
|
@ -771,7 +771,7 @@ export class AgentSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let hookCompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number } | undefined;
|
let hookCompaction: CompactionResult | undefined;
|
||||||
let fromHook = false;
|
let fromHook = false;
|
||||||
|
|
||||||
if (this._hookRunner?.hasHandlers("session")) {
|
if (this._hookRunner?.hasHandlers("session")) {
|
||||||
|
|
@ -806,12 +806,14 @@ export class AgentSession {
|
||||||
let summary: string;
|
let summary: string;
|
||||||
let firstKeptEntryId: string;
|
let firstKeptEntryId: string;
|
||||||
let tokensBefore: number;
|
let tokensBefore: number;
|
||||||
|
let details: unknown;
|
||||||
|
|
||||||
if (hookCompaction) {
|
if (hookCompaction) {
|
||||||
// Hook provided compaction content
|
// Hook provided compaction content
|
||||||
summary = hookCompaction.summary;
|
summary = hookCompaction.summary;
|
||||||
firstKeptEntryId = hookCompaction.firstKeptEntryId;
|
firstKeptEntryId = hookCompaction.firstKeptEntryId;
|
||||||
tokensBefore = hookCompaction.tokensBefore;
|
tokensBefore = hookCompaction.tokensBefore;
|
||||||
|
details = hookCompaction.details;
|
||||||
} else {
|
} else {
|
||||||
// Generate compaction result
|
// Generate compaction result
|
||||||
const result = await compact(
|
const result = await compact(
|
||||||
|
|
@ -825,13 +827,14 @@ export class AgentSession {
|
||||||
summary = result.summary;
|
summary = result.summary;
|
||||||
firstKeptEntryId = result.firstKeptEntryId;
|
firstKeptEntryId = result.firstKeptEntryId;
|
||||||
tokensBefore = result.tokensBefore;
|
tokensBefore = result.tokensBefore;
|
||||||
|
details = result.details;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._compactionAbortController.signal.aborted) {
|
if (this._compactionAbortController.signal.aborted) {
|
||||||
throw new Error("Compaction cancelled");
|
throw new Error("Compaction cancelled");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore);
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details);
|
||||||
const newEntries = this.sessionManager.getEntries();
|
const newEntries = this.sessionManager.getEntries();
|
||||||
const sessionContext = this.sessionManager.buildSessionContext();
|
const sessionContext = this.sessionManager.buildSessionContext();
|
||||||
this.agent.replaceMessages(sessionContext.messages);
|
this.agent.replaceMessages(sessionContext.messages);
|
||||||
|
|
@ -855,8 +858,10 @@ export class AgentSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tokensBefore,
|
|
||||||
summary,
|
summary,
|
||||||
|
firstKeptEntryId,
|
||||||
|
tokensBefore,
|
||||||
|
details,
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
this._compactionAbortController = null;
|
this._compactionAbortController = null;
|
||||||
|
|
@ -952,7 +957,7 @@ export class AgentSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let hookCompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number } | undefined;
|
let hookCompaction: CompactionResult | undefined;
|
||||||
let fromHook = false;
|
let fromHook = false;
|
||||||
|
|
||||||
if (this._hookRunner?.hasHandlers("session")) {
|
if (this._hookRunner?.hasHandlers("session")) {
|
||||||
|
|
@ -988,12 +993,14 @@ export class AgentSession {
|
||||||
let summary: string;
|
let summary: string;
|
||||||
let firstKeptEntryId: string;
|
let firstKeptEntryId: string;
|
||||||
let tokensBefore: number;
|
let tokensBefore: number;
|
||||||
|
let details: unknown;
|
||||||
|
|
||||||
if (hookCompaction) {
|
if (hookCompaction) {
|
||||||
// Hook provided compaction content
|
// Hook provided compaction content
|
||||||
summary = hookCompaction.summary;
|
summary = hookCompaction.summary;
|
||||||
firstKeptEntryId = hookCompaction.firstKeptEntryId;
|
firstKeptEntryId = hookCompaction.firstKeptEntryId;
|
||||||
tokensBefore = hookCompaction.tokensBefore;
|
tokensBefore = hookCompaction.tokensBefore;
|
||||||
|
details = hookCompaction.details;
|
||||||
} else {
|
} else {
|
||||||
// Generate compaction result
|
// Generate compaction result
|
||||||
const compactResult = await compact(
|
const compactResult = await compact(
|
||||||
|
|
@ -1006,6 +1013,7 @@ export class AgentSession {
|
||||||
summary = compactResult.summary;
|
summary = compactResult.summary;
|
||||||
firstKeptEntryId = compactResult.firstKeptEntryId;
|
firstKeptEntryId = compactResult.firstKeptEntryId;
|
||||||
tokensBefore = compactResult.tokensBefore;
|
tokensBefore = compactResult.tokensBefore;
|
||||||
|
details = compactResult.details;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._autoCompactionAbortController.signal.aborted) {
|
if (this._autoCompactionAbortController.signal.aborted) {
|
||||||
|
|
@ -1013,7 +1021,7 @@ export class AgentSession {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore);
|
this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details);
|
||||||
const newEntries = this.sessionManager.getEntries();
|
const newEntries = this.sessionManager.getEntries();
|
||||||
const sessionContext = this.sessionManager.buildSessionContext();
|
const sessionContext = this.sessionManager.buildSessionContext();
|
||||||
this.agent.replaceMessages(sessionContext.messages);
|
this.agent.replaceMessages(sessionContext.messages);
|
||||||
|
|
@ -1037,8 +1045,10 @@ export class AgentSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: CompactionResult = {
|
const result: CompactionResult = {
|
||||||
tokensBefore,
|
|
||||||
summary,
|
summary,
|
||||||
|
firstKeptEntryId,
|
||||||
|
tokensBefore,
|
||||||
|
details,
|
||||||
};
|
};
|
||||||
this._emit({ type: "auto_compaction_end", result, aborted: false, willRetry });
|
this._emit({ type: "auto_compaction_end", result, aborted: false, willRetry });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,12 @@ import { messageTransformer } from "./messages.js";
|
||||||
import type { CompactionEntry, SessionEntry } from "./session-manager.js";
|
import type { CompactionEntry, SessionEntry } from "./session-manager.js";
|
||||||
|
|
||||||
/** Result from compact() - SessionManager adds uuid/parentUuid when saving */
|
/** Result from compact() - SessionManager adds uuid/parentUuid when saving */
|
||||||
export interface CompactionResult {
|
export interface CompactionResult<T = unknown> {
|
||||||
summary: string;
|
summary: string;
|
||||||
firstKeptEntryId: string;
|
firstKeptEntryId: string;
|
||||||
tokensBefore: number;
|
tokensBefore: number;
|
||||||
|
/** Hook-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
|
||||||
|
details?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ export {
|
||||||
type AgentSessionConfig,
|
type AgentSessionConfig,
|
||||||
type AgentSessionEvent,
|
type AgentSessionEvent,
|
||||||
type AgentSessionEventListener,
|
type AgentSessionEventListener,
|
||||||
type CompactionResult,
|
|
||||||
type ModelCycleResult,
|
type ModelCycleResult,
|
||||||
type PromptOptions,
|
type PromptOptions,
|
||||||
type SessionStats,
|
type SessionStats,
|
||||||
} from "./agent-session.js";
|
} from "./agent-session.js";
|
||||||
export { type BashExecutorOptions, type BashResult, executeBash } from "./bash-executor.js";
|
export { type BashExecutorOptions, type BashResult, executeBash } from "./bash-executor.js";
|
||||||
|
export type { CompactionResult } from "./compaction.js";
|
||||||
export {
|
export {
|
||||||
type CustomAgentTool,
|
type CustomAgentTool,
|
||||||
type CustomToolFactory,
|
type CustomToolFactory,
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,13 @@ export interface ModelChangeEntry extends SessionEntryBase {
|
||||||
modelId: string;
|
modelId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompactionEntry extends SessionEntryBase {
|
export interface CompactionEntry<T = unknown> extends SessionEntryBase {
|
||||||
type: "compaction";
|
type: "compaction";
|
||||||
summary: string;
|
summary: string;
|
||||||
firstKeptEntryId: string;
|
firstKeptEntryId: string;
|
||||||
tokensBefore: number;
|
tokensBefore: number;
|
||||||
|
/** Hook-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
|
||||||
|
details?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BranchSummaryEntry extends SessionEntryBase {
|
export interface BranchSummaryEntry extends SessionEntryBase {
|
||||||
|
|
@ -592,8 +594,8 @@ export class SessionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Append a compaction summary as child of current leaf, then advance leaf. Returns entry id. */
|
/** Append a compaction summary as child of current leaf, then advance leaf. Returns entry id. */
|
||||||
appendCompaction(summary: string, firstKeptEntryId: string, tokensBefore: number): string {
|
appendCompaction<T = unknown>(summary: string, firstKeptEntryId: string, tokensBefore: number, details?: T): string {
|
||||||
const entry: CompactionEntry = {
|
const entry: CompactionEntry<T> = {
|
||||||
type: "compaction",
|
type: "compaction",
|
||||||
id: generateId(this.byId),
|
id: generateId(this.byId),
|
||||||
parentId: this.leafId || null,
|
parentId: this.leafId || null,
|
||||||
|
|
@ -601,6 +603,7 @@ export class SessionManager {
|
||||||
summary,
|
summary,
|
||||||
firstKeptEntryId,
|
firstKeptEntryId,
|
||||||
tokensBefore,
|
tokensBefore,
|
||||||
|
details,
|
||||||
};
|
};
|
||||||
this._appendEntry(entry);
|
this._appendEntry(entry);
|
||||||
return entry.id;
|
return entry.id;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ export {
|
||||||
type AgentSessionConfig,
|
type AgentSessionConfig,
|
||||||
type AgentSessionEvent,
|
type AgentSessionEvent,
|
||||||
type AgentSessionEventListener,
|
type AgentSessionEventListener,
|
||||||
type CompactionResult,
|
|
||||||
type ModelCycleResult,
|
type ModelCycleResult,
|
||||||
type PromptOptions,
|
type PromptOptions,
|
||||||
type SessionStats,
|
type SessionStats,
|
||||||
|
|
@ -13,6 +12,7 @@ export {
|
||||||
export { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from "./core/auth-storage.js";
|
export { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from "./core/auth-storage.js";
|
||||||
// Compaction
|
// Compaction
|
||||||
export {
|
export {
|
||||||
|
type CompactionResult,
|
||||||
type CutPointResult,
|
type CutPointResult,
|
||||||
calculateContextTokens,
|
calculateContextTokens,
|
||||||
compact,
|
compact,
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@
|
||||||
import { type ChildProcess, spawn } from "node:child_process";
|
import { type ChildProcess, spawn } from "node:child_process";
|
||||||
import * as readline from "node:readline";
|
import * as readline from "node:readline";
|
||||||
import type { AgentEvent, AppMessage, Attachment, ThinkingLevel } from "@mariozechner/pi-agent-core";
|
import type { AgentEvent, AppMessage, Attachment, ThinkingLevel } from "@mariozechner/pi-agent-core";
|
||||||
import type { CompactionResult, SessionStats } from "../../core/agent-session.js";
|
import type { SessionStats } from "../../core/agent-session.js";
|
||||||
import type { BashResult } from "../../core/bash-executor.js";
|
import type { BashResult } from "../../core/bash-executor.js";
|
||||||
|
import type { CompactionResult } from "../../core/compaction.js";
|
||||||
import type { RpcCommand, RpcResponse, RpcSessionState } from "./rpc-types.js";
|
import type { RpcCommand, RpcResponse, RpcSessionState } from "./rpc-types.js";
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@
|
||||||
|
|
||||||
import type { AppMessage, Attachment, ThinkingLevel } from "@mariozechner/pi-agent-core";
|
import type { AppMessage, Attachment, ThinkingLevel } from "@mariozechner/pi-agent-core";
|
||||||
import type { Model } from "@mariozechner/pi-ai";
|
import type { Model } from "@mariozechner/pi-ai";
|
||||||
import type { CompactionResult, SessionStats } from "../../core/agent-session.js";
|
import type { SessionStats } from "../../core/agent-session.js";
|
||||||
import type { BashResult } from "../../core/bash-executor.js";
|
import type { BashResult } from "../../core/bash-executor.js";
|
||||||
|
import type { CompactionResult } from "../../core/compaction.js";
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// RPC Commands (stdin)
|
// RPC Commands (stdin)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue