mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
Each branch is a top-level conversation forked from another at an entry; the session-scoped p2.main document lists them and names the current one. Earlier branches are read-only. The main agent's per-agent state (subagents, artifacts, voice and chat mode, pending move) stays on conversation 1 for every branch. - tree plugin: SessionTree (tree, navigate, select); the worker reports main and branches to the catalog (Sessions v4, catalog schema 4) - TUI: /tree and double Escape (pi's default "tree"), a tree selector adapted from coding-agent - web: Edit on user messages, Branch from here on answers, branch rows under the session in the sidebar - main-agent checks use the branch list instead of conversation 1 - search indexes each branch's own entries Design: apps/p2/docs/tree.md
57 lines
2.7 KiB
TypeScript
57 lines
2.7 KiB
TypeScript
import { defineExtension, defineTool, section } from "@earendil-works/pi-durable";
|
|
import { arr, obj, opt, str } from "@earendil-works/pi-schema";
|
|
import type { Context, DevResult } from "@p2/app/v1";
|
|
import { isTopLevel, originOf } from "@p2/app/v1/worker";
|
|
|
|
export function createReloadExtension(
|
|
reload: (plugins: readonly string[] | undefined, context: Context) => Promise<DevResult>,
|
|
project: boolean | (() => boolean),
|
|
) {
|
|
const permitted = () => (typeof project === "function" ? project() : project);
|
|
return defineExtension({
|
|
name: "p2-dev-tools",
|
|
sections: [
|
|
section("p2-development", async (input, context) =>
|
|
permitted() && (await isTopLevel(input.read, input.conversationId, context))
|
|
? "p2 backend saves only mark dirty. After editing p2 plugins, use reload_plugins to apply dirty plugins (or name plugins). Web/UI edits are plugin edits too: reload_plugins rebuilds the web bundle and open pages reload onto it. Runtime edits need an explicit `npm run reload -- --restart` from apps/p2 with this server's P2_DATA_DIR. Only owner-initiated turns may use reload_plugins, never guests, peers or unattributed input."
|
|
: undefined,
|
|
),
|
|
],
|
|
tools: [
|
|
defineTool({
|
|
name: "reload_plugins",
|
|
description:
|
|
"Explicitly reload p2's dirty backend plugins on the server and all running workers; optional plugins selects names. Returns the committed selection or refusal reason. Only owner-initiated root turns in a local p2 checkout. Runtime code needs npm run reload -- --restart instead.",
|
|
parameters: obj(
|
|
{
|
|
plugins: opt(arr(str({ minLength: 1, maxLength: 100 }), { maxItems: 100 })),
|
|
},
|
|
{ open: true },
|
|
),
|
|
// Do not replay a code upgrade after a crash; the caller can explicitly request another reload.
|
|
replay: "unsafe",
|
|
execute: async (args, api, context) => {
|
|
const reply = (message: string, ok: boolean) => ({
|
|
output: [{ type: "text" as const, text: message }],
|
|
details: { ok },
|
|
isError: !ok,
|
|
});
|
|
if (!permitted() || !(await isTopLevel(api, api.conversationId, context)))
|
|
return reply("reload_plugins requires a root session working on a local p2 checkout", false);
|
|
if ((await originOf(api, context)) !== "owner")
|
|
return reply(
|
|
"reload_plugins requires owner-initiated input; guests, peers and unattributed input cannot trigger upgrades",
|
|
false,
|
|
);
|
|
try {
|
|
const result = await reload(args.plugins, context);
|
|
return reply(result.message, result.ok);
|
|
} catch (error) {
|
|
if (context.abortSignal?.aborted) throw error;
|
|
return reply(error instanceof Error ? error.message : String(error), false);
|
|
}
|
|
},
|
|
}),
|
|
],
|
|
});
|
|
}
|