mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-11 10:57:07 +00:00
Give plugins an agent's background work count and a background flag on hooks (#9362)
* feat(plugins): give plugins the agent's sidebar indicator and background flag Plugin UI re-derived agent state from status and attention fields and could not see background work, so it showed agents as done while a shell they left running was still live. Agent snapshots now carry the sidebar's own bucket as indicator/indicatorLabel, computed by the same function the sidebar uses so the two cannot drift. Lifecycle hooks and the agent.open.before request now carry `background`, so a hook can skip a hidden helper agent without a lookup. A hook cannot change it; an omitted value means unchanged, like metadata. `finished` attention still means the turn finished; background work is the indicator's concern. * fix(plugins): keep session-open hooks that omit background typechecking A hook that returns its own object instead of spreading the request has no background field; the result type now treats it as optional, like metadata, matching the runtime where an omitted value means unchanged. * refactor(plugins): give agent snapshots the background work count instead of an indicator * fix(plugins): keep legacy session-open hooks that omit background typechecking
This commit is contained in:
@@ -370,6 +370,17 @@ describe("plugin agent tools SDK boundary", () => {
|
||||
...request.mcpServers, company: { type: "http", url: "https://tools.example.com/mcp" },
|
||||
} };
|
||||
});
|
||||
server.addHook("agent.open.before", async ({ request }) => {
|
||||
const background: boolean = request.background;
|
||||
if (background) return;
|
||||
// A hook written before background and metadata returns its own object without them.
|
||||
return {
|
||||
agentId: request.agentId, workspaceId: request.workspaceId, provider: request.provider,
|
||||
cwd: request.cwd, reason: request.reason, purpose: request.purpose, title: request.title,
|
||||
labels: request.labels, tools: request.tools, env: request.env,
|
||||
mcpServers: request.mcpServers,
|
||||
};
|
||||
});
|
||||
return () => {};
|
||||
}
|
||||
`),
|
||||
@@ -429,6 +440,12 @@ describe("lifecycle hook SDK types", () => {
|
||||
// @ts-expect-error A prepare handler must return the whole request or undefined.
|
||||
server.addHook("agent.create.before", async () => ({ invalid: true }));
|
||||
server.before("agent.session_open", ({ request }) => request);
|
||||
server.before("agent.session_open", ({ request }) => ({
|
||||
agentId: request.agentId, workspaceId: request.workspaceId, provider: request.provider,
|
||||
cwd: request.cwd, reason: request.reason, purpose: request.purpose, title: request.title,
|
||||
labels: request.labels, metadata: request.metadata, tools: request.tools, env: request.env,
|
||||
mcpServers: request.mcpServers,
|
||||
}));
|
||||
server.on("agent.turn_ended", () => {});
|
||||
return () => {};
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ export interface PluginAgentSnapshot {
|
||||
readonly workspaceId: string;
|
||||
readonly provider: string;
|
||||
readonly status: "initializing" | "idle" | "running" | "error" | "closed";
|
||||
/** Shells and monitors the agent left running after its turn. 0 when none are live. */
|
||||
readonly backgroundWork: { readonly count: number };
|
||||
/** A helper hidden from tabs, lists and attention. Fixed at creation. */
|
||||
readonly background: boolean;
|
||||
readonly createdAt: string;
|
||||
readonly updatedAt: string;
|
||||
readonly lastActivityAt: string;
|
||||
|
||||
@@ -71,6 +71,8 @@ export interface PluginHookAgent {
|
||||
provider: string;
|
||||
cwd: string;
|
||||
title: string | null;
|
||||
/** A helper hidden from tabs, lists and attention. Fixed at creation. */
|
||||
readonly background: boolean;
|
||||
/** Data plugins stored on the agent. Paseo never reads a key. */
|
||||
metadata: Record<string, JsonValue>;
|
||||
}
|
||||
@@ -83,6 +85,8 @@ export interface PluginSessionOpenRequest {
|
||||
reason: "create" | "resume" | "refresh" | "import";
|
||||
purpose: "interactive" | "history";
|
||||
readonly title: string | null;
|
||||
/** A helper hidden from tabs, lists and attention. Fixed at creation. */
|
||||
readonly background: boolean;
|
||||
readonly labels: Readonly<Record<string, string>>;
|
||||
readonly metadata: Readonly<Record<string, JsonValue>>;
|
||||
tools: AgentToolDefinition[];
|
||||
@@ -134,7 +138,9 @@ export interface PluginHookRequests {
|
||||
|
||||
/** What a hook may return. An omitted `metadata` stays as it was. */
|
||||
type PluginHookRequestResults = Omit<PluginHookRequests, "agent.open.before"> & {
|
||||
"agent.open.before": Omit<PluginSessionOpenRequest, "metadata"> & {
|
||||
// Read-only fields added after the first SDK stay optional in a result; omitted means unchanged.
|
||||
"agent.open.before": Omit<PluginSessionOpenRequest, "metadata" | "background"> & {
|
||||
readonly background?: boolean;
|
||||
metadata?: Readonly<Record<string, JsonValue>>;
|
||||
};
|
||||
};
|
||||
@@ -184,7 +190,7 @@ export interface PluginLifecycleRegistration {
|
||||
handler: (
|
||||
input: { request: PluginBeforeRequests[Name] },
|
||||
context: PluginHookContext,
|
||||
) => PluginBeforeRequests[Name] | void | Promise<PluginBeforeRequests[Name] | void>,
|
||||
) => PluginBeforeResults[Name] | void | Promise<PluginBeforeResults[Name] | void>,
|
||||
): () => void;
|
||||
}
|
||||
|
||||
@@ -229,6 +235,9 @@ export type PluginLifecycleEvents = {
|
||||
export type PluginBeforeRequests = {
|
||||
[Name in keyof typeof legacyRequests]: PluginHookRequests[(typeof legacyRequests)[Name]];
|
||||
};
|
||||
type PluginBeforeResults = {
|
||||
[Name in keyof typeof legacyRequests]: PluginHookRequestResults[(typeof legacyRequests)[Name]];
|
||||
};
|
||||
|
||||
/** @internal SDK adapter for the daemon's hook registry. */
|
||||
export function createLifecycleRegistration(
|
||||
|
||||
Reference in New Issue
Block a user