mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 08:02:17 +00:00
Merge hooks and custom-tools into unified extensions system (#454)
Breaking changes: - Settings: 'hooks' and 'customTools' arrays replaced with 'extensions' - CLI: '--hook' and '--tool' flags replaced with '--extension' / '-e' - API: HookMessage renamed to CustomMessage, role 'hookMessage' to 'custom' - API: FileSlashCommand renamed to PromptTemplate - API: discoverSlashCommands() renamed to discoverPromptTemplates() - Directories: commands/ renamed to prompts/ for prompt templates Migration: - Session version bumped to 3 (auto-migrates v2 sessions) - Old 'hookMessage' role entries converted to 'custom' Structural changes: - src/core/hooks/ and src/core/custom-tools/ merged into src/core/extensions/ - src/core/slash-commands.ts renamed to src/core/prompt-templates.ts - examples/hooks/ and examples/custom-tools/ merged into examples/extensions/ - docs/hooks.md and docs/custom-tools.md merged into docs/extensions.md New test coverage: - test/extensions-runner.test.ts (10 tests) - test/extensions-discovery.test.ts (26 tests) - test/prompt-templates.test.ts
This commit is contained in:
parent
9794868b38
commit
c6fc084534
112 changed files with 2842 additions and 6747 deletions
|
|
@ -17,13 +17,13 @@ import { join, resolve } from "path";
|
|||
import { getAgentDir as getDefaultAgentDir } from "../config.js";
|
||||
import {
|
||||
type BashExecutionMessage,
|
||||
type CustomMessage,
|
||||
createBranchSummaryMessage,
|
||||
createCompactionSummaryMessage,
|
||||
createHookMessage,
|
||||
type HookMessage,
|
||||
createCustomMessage,
|
||||
} from "./messages.js";
|
||||
|
||||
export const CURRENT_SESSION_VERSION = 2;
|
||||
export const CURRENT_SESSION_VERSION = 3;
|
||||
|
||||
export interface SessionHeader {
|
||||
type: "session";
|
||||
|
|
@ -66,9 +66,9 @@ export interface CompactionEntry<T = unknown> extends SessionEntryBase {
|
|||
summary: string;
|
||||
firstKeptEntryId: string;
|
||||
tokensBefore: number;
|
||||
/** Hook-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
|
||||
/** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
|
||||
details?: T;
|
||||
/** True if generated by a hook, undefined/false if pi-generated (backward compatible) */
|
||||
/** True if generated by an extension, undefined/false if pi-generated (backward compatible) */
|
||||
fromHook?: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -76,17 +76,17 @@ export interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {
|
|||
type: "branch_summary";
|
||||
fromId: string;
|
||||
summary: string;
|
||||
/** Hook-specific data (not sent to LLM) */
|
||||
/** Extension-specific data (not sent to LLM) */
|
||||
details?: T;
|
||||
/** True if generated by a hook, false if pi-generated */
|
||||
/** True if generated by an extension, false if pi-generated */
|
||||
fromHook?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom entry for hooks to store hook-specific data in the session.
|
||||
* Use customType to identify your hook's entries.
|
||||
* Custom entry for extensions to store extension-specific data in the session.
|
||||
* Use customType to identify your extension's entries.
|
||||
*
|
||||
* Purpose: Persist hook state across session reloads. On reload, hooks can
|
||||
* Purpose: Persist extension state across session reloads. On reload, extensions can
|
||||
* scan entries for their customType and reconstruct internal state.
|
||||
*
|
||||
* Does NOT participate in LLM context (ignored by buildSessionContext).
|
||||
|
|
@ -106,12 +106,12 @@ export interface LabelEntry extends SessionEntryBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Custom message entry for hooks to inject messages into LLM context.
|
||||
* Use customType to identify your hook's entries.
|
||||
* Custom message entry for extensions to inject messages into LLM context.
|
||||
* Use customType to identify your extension's entries.
|
||||
*
|
||||
* Unlike CustomEntry, this DOES participate in LLM context.
|
||||
* The content is converted to a user message in buildSessionContext().
|
||||
* Use details for hook-specific metadata (not sent to LLM).
|
||||
* Use details for extension-specific metadata (not sent to LLM).
|
||||
*
|
||||
* display controls TUI rendering:
|
||||
* - false: hidden entirely
|
||||
|
|
@ -218,8 +218,23 @@ function migrateV1ToV2(entries: FileEntry[]): void {
|
|||
}
|
||||
}
|
||||
|
||||
// Add future migrations here:
|
||||
// function migrateV2ToV3(entries: FileEntry[]): void { ... }
|
||||
/** Migrate v2 → v3: rename hookMessage role to custom. Mutates in place. */
|
||||
function migrateV2ToV3(entries: FileEntry[]): void {
|
||||
for (const entry of entries) {
|
||||
if (entry.type === "session") {
|
||||
entry.version = 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update message entries with hookMessage role
|
||||
if (entry.type === "message") {
|
||||
const msgEntry = entry as SessionMessageEntry;
|
||||
if (msgEntry.message && (msgEntry.message as { role: string }).role === "hookMessage") {
|
||||
(msgEntry.message as { role: string }).role = "custom";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all necessary migrations to bring entries to current version.
|
||||
|
|
@ -232,7 +247,7 @@ function migrateToCurrentVersion(entries: FileEntry[]): boolean {
|
|||
if (version >= CURRENT_SESSION_VERSION) return false;
|
||||
|
||||
if (version < 2) migrateV1ToV2(entries);
|
||||
// if (version < 3) migrateV2ToV3(entries);
|
||||
if (version < 3) migrateV2ToV3(entries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -342,7 +357,7 @@ export function buildSessionContext(
|
|||
messages.push(entry.message);
|
||||
} else if (entry.type === "custom_message") {
|
||||
messages.push(
|
||||
createHookMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp),
|
||||
createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp),
|
||||
);
|
||||
} else if (entry.type === "branch_summary" && entry.summary) {
|
||||
messages.push(createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp));
|
||||
|
|
@ -609,7 +624,7 @@ export class SessionManager {
|
|||
* so it is easier to find them.
|
||||
* These need to be appended via appendCompaction() and appendBranchSummary() methods.
|
||||
*/
|
||||
appendMessage(message: Message | HookMessage | BashExecutionMessage): string {
|
||||
appendMessage(message: Message | CustomMessage | BashExecutionMessage): string {
|
||||
const entry: SessionMessageEntry = {
|
||||
type: "message",
|
||||
id: generateId(this.byId),
|
||||
|
|
@ -671,7 +686,7 @@ export class SessionManager {
|
|||
return entry.id;
|
||||
}
|
||||
|
||||
/** Append a custom entry (for hooks) as child of current leaf, then advance leaf. Returns entry id. */
|
||||
/** Append a custom entry (for extensions) as child of current leaf, then advance leaf. Returns entry id. */
|
||||
appendCustomEntry(customType: string, data?: unknown): string {
|
||||
const entry: CustomEntry = {
|
||||
type: "custom",
|
||||
|
|
@ -686,11 +701,11 @@ export class SessionManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Append a custom message entry (for hooks) that participates in LLM context.
|
||||
* @param customType Hook identifier for filtering on reload
|
||||
* Append a custom message entry (for extensions) that participates in LLM context.
|
||||
* @param customType Extension identifier for filtering on reload
|
||||
* @param content Message content (string or TextContent/ImageContent array)
|
||||
* @param display Whether to show in TUI (true = styled display, false = hidden)
|
||||
* @param details Optional hook-specific metadata (not sent to LLM)
|
||||
* @param details Optional extension-specific metadata (not sent to LLM)
|
||||
* @returns Entry id
|
||||
*/
|
||||
appendCustomMessageEntry<T = unknown>(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue