mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-02 22:52:58 +00:00
fix(plugins): omitted metadata in a hook result means unchanged
An agent.create.before hook that returned its own { config, env } dropped
metadata that an earlier hook in the chain had added, because only the
caller's original metadata was restored at the end. The omitted field is
now carried forward at each hook handoff, as agent.open.before already
does.
The agent.open.before result type also required metadata, so an older
TypeScript plugin that returned its own object no longer compiled even
though the daemon accepted it. The result type now makes it optional.
This commit is contained in:
@@ -283,6 +283,10 @@ describe("lifecycle hook SDK types", () => {
|
||||
server.addHook("workspace.create.before", async ({ request }) => ({ ...request, title: "Changed" }));
|
||||
server.addHook("workspace.create.before", async () => {});
|
||||
server.addHook("agent.open.before", async () => {});
|
||||
server.addHook("agent.open.before", async ({ request }) => {
|
||||
const { metadata, ...withoutMetadata } = request;
|
||||
return withoutMetadata;
|
||||
});
|
||||
server.addHook("agent.turn.after", async ({ agent, outcome, timeline }, { paseo, signal }) => {
|
||||
const kind: "completed" | "failed" | "canceled" = outcome.kind;
|
||||
// @ts-expect-error Notification payloads have no creation request.
|
||||
|
||||
@@ -132,10 +132,17 @@ export interface PluginHookRequests {
|
||||
"workspace.create.before": Omit<WorkspaceCreateRequest, "type" | "requestId">;
|
||||
}
|
||||
|
||||
/** What a hook may return: a field it leaves out stays as it was. */
|
||||
type PluginHookRequestResults = Omit<PluginHookRequests, "agent.open.before"> & {
|
||||
"agent.open.before": Omit<PluginSessionOpenRequest, "metadata"> & {
|
||||
metadata?: Readonly<Record<string, JsonValue>>;
|
||||
};
|
||||
};
|
||||
|
||||
export type PluginHooks = {
|
||||
[Name in keyof PluginHookRequests]: {
|
||||
request: PluginHookRequests[Name];
|
||||
result: PluginHookRequests[Name];
|
||||
result: PluginHookRequestResults[Name];
|
||||
};
|
||||
} & { [Name in keyof PluginHookEvents]: { payload: PluginHookEvents[Name] } } & {
|
||||
"worktree.create.after": { payload: { worktree: PluginHookWorktree } };
|
||||
|
||||
@@ -101,6 +101,30 @@ test("session-open hooks reject changes to session identity instead of silently
|
||||
).rejects.toThrow("agent.open.before hooks can only change env");
|
||||
});
|
||||
|
||||
test("a create hook that omits metadata passes on what an earlier hook added", async () => {
|
||||
const hooks = new PluginHookHandlers(() => {});
|
||||
hooks.addHook("agent.create.before", async ({ request }) => ({
|
||||
...request,
|
||||
metadata: { ...request.metadata, "new.plugin": { ticket: 42 } },
|
||||
}));
|
||||
hooks.addHook("agent.create.before", async ({ request }) => ({
|
||||
config: request.config,
|
||||
env: request.env,
|
||||
}));
|
||||
const seen: unknown[] = [];
|
||||
hooks.addHook("agent.create.before", async ({ request }) => {
|
||||
seen.push(request.metadata);
|
||||
});
|
||||
const result = await hooks.invoke(
|
||||
"operation",
|
||||
"agent.create.before",
|
||||
{ config: { provider: "codex", cwd: "/project" }, env: {}, metadata: { original: true } },
|
||||
paseo,
|
||||
);
|
||||
expect(result).toMatchObject({ metadata: { original: true, "new.plugin": { ticket: 42 } } });
|
||||
expect(seen).toEqual([{ original: true, "new.plugin": { ticket: 42 } }]);
|
||||
});
|
||||
|
||||
test("a session-open hook that returns its own object without metadata leaves metadata unchanged", async () => {
|
||||
const hooks = new PluginHookHandlers(() => {});
|
||||
hooks.addHook("agent.open.before", async ({ request }) => ({
|
||||
|
||||
@@ -249,6 +249,8 @@ export async function validateBeforeResult<Name extends keyof PluginWireBeforeRe
|
||||
if (previous.config.cwd !== next.config.cwd) {
|
||||
throw new Error("agent.create.before hooks cannot change the workspace directory");
|
||||
}
|
||||
// Omitted means unchanged: the next hook sees what this one was handed.
|
||||
return { ...result, metadata: next.metadata ?? previous.metadata };
|
||||
}
|
||||
if (name === "worktree.create.before") {
|
||||
const previous = beforeSchemas["worktree.create.before"].parse(input);
|
||||
|
||||
Reference in New Issue
Block a user