mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 04:01:56 +00:00
Add context event for non-destructive message modification before LLM calls
- Add contextTransform option to Agent (runs before messageTransformer) - Deep copy messages before passing to contextTransform (modifications are ephemeral) - Add ContextEvent and ContextEventResult types - Add emitContext() to HookRunner (chains multiple handlers) - Wire up in sdk.ts when creating Agent with hooks Enables dynamic context pruning: hooks can modify messages sent to LLM without changing session data. See discussion #330.
This commit is contained in:
parent
9e165d1d81
commit
77fe3f1a13
6 changed files with 89 additions and 2 deletions
|
|
@ -2,10 +2,13 @@
|
|||
* Hook runner - executes hooks and manages their lifecycle.
|
||||
*/
|
||||
|
||||
import type { AppMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { ModelRegistry } from "../model-registry.js";
|
||||
import type { SessionManager } from "../session-manager.js";
|
||||
import type { AppendEntryHandler, LoadedHook, SendMessageHandler } from "./loader.js";
|
||||
import type {
|
||||
ContextEvent,
|
||||
ContextEventResult,
|
||||
HookError,
|
||||
HookEvent,
|
||||
HookEventContext,
|
||||
|
|
@ -304,4 +307,43 @@ export class HookRunner {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a context event to all hooks.
|
||||
* Handlers are chained - each gets the previous handler's output (if any).
|
||||
* Returns the final modified messages, or undefined if no modifications.
|
||||
*/
|
||||
async emitContext(messages: AppMessage[]): Promise<AppMessage[] | undefined> {
|
||||
const ctx = this.createContext();
|
||||
let currentMessages = messages;
|
||||
let modified = false;
|
||||
|
||||
for (const hook of this.hooks) {
|
||||
const handlers = hook.handlers.get("context");
|
||||
if (!handlers || handlers.length === 0) continue;
|
||||
|
||||
for (const handler of handlers) {
|
||||
try {
|
||||
const event: ContextEvent = { type: "context", messages: currentMessages };
|
||||
const timeout = createTimeout(this.timeout);
|
||||
const handlerResult = await Promise.race([handler(event, ctx), timeout.promise]);
|
||||
timeout.clear();
|
||||
|
||||
if (handlerResult && (handlerResult as ContextEventResult).messages) {
|
||||
currentMessages = (handlerResult as ContextEventResult).messages!;
|
||||
modified = true;
|
||||
}
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
this.emitError({
|
||||
hookPath: hook.path,
|
||||
event: "context",
|
||||
error: message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modified ? currentMessages : undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue