import type { AgentToolResult, ExtensionAPI, ExtensionContext } from "@oh-my-pi/pi-coding-agent"; import { Text } from "@oh-my-pi/pi-tui"; import { changeChannel, changeReservation, DeskRefusal, type DeskState, entryType, inspect, query, reconstruct, scope, type Snapshot } from "./desk"; interface DeskDetails { ok: boolean; scope: typeof scope; revision?: string; agentEnabled?: boolean; code?: string; data?: unknown; } export default async function seedReservations(pi: ExtensionAPI) { const description = await Bun.file(new URL("./tool.txt", import.meta.url)).text(); const z = pi.zod; const parameters = z.object({ op: z.enum(["discover", "inspect", "query", "act"]), id: z.string().optional(), family: z.string().optional(), action: z.enum(["reserve", "release"]).optional(), packets: z.number().int().optional(), expectedRevision: z.string().optional(), }); function announce(ctx: ExtensionContext, text: string, failed = false) { if (ctx.hasUI) ctx.ui.notify(text, failed ? "warning" : "info"); else pi.sendMessage({ customType: "seed-desk", content: text, display: true }, { triggerTurn: false }); } function refresh(ctx: ExtensionContext) { try { const state = reconstruct(ctx); if (ctx.hasUI) ctx.ui.setStatus("seed-desk", `Seed desk: session-local | agent ${state.agentEnabled ? "on" : "off"}`); } catch (error) { if (ctx.hasUI) ctx.ui.setStatus("seed-desk", "Seed desk: invalid branch state"); announce(ctx, error instanceof Error ? error.message : "Cannot read seed-desk state.", true); } } function commit(ctx: ExtensionContext, snapshot: Snapshot, action: string): DeskState { // No await between reconstruction, validation and append: calls cannot interleave here. pi.appendEntry(entryType, snapshot); const state = reconstruct(ctx); refresh(ctx); pi.events.emit(changeChannel, { sessionId: ctx.sessionManager.getSessionId(), revision: state.revision, scope, action }); return state; } function reply(state: DeskState, text: string, data?: unknown): AgentToolResult { return { content: [{ type: "text", text: `${text}\nScope: fictional session branch only; real stock is unchanged.\nRevision: ${state.revision}` }], details: { ok: true, scope, revision: state.revision, agentEnabled: state.agentEnabled, data }, }; } pi.registerTool({ name: "seed_desk", label: "Seed desk", description, parameters, approval: "write", loadMode: "essential", async execute(_callId, params, signal, _onUpdate, ctx) { signal?.throwIfAborted(); let state: DeskState | undefined; try { state = reconstruct(ctx); if (params.op === "discover") { return reply(state, "Use query for IDs and a revision; inspect for one seed; act for reserve/release. Only /seeds agent on can grant the agent permission.", { operations: ["discover", "inspect", "query", "act"], actRequires: ["action", "id", "packets", "expectedRevision", "human-granted agent permission"], }); } if (params.op === "query") { const seeds = query(state, params.family); return reply(state, seeds.length ? seeds.map(seed => `${seed.id}: ${seed.available} available, ${seed.reserved} reserved`).join("\n") : "No seeds match that family.", seeds); } if (params.op === "inspect") { const seed = inspect(params.id ?? "", state); return reply(state, `${seed.name}: ${seed.available} available, ${seed.reserved} reserved`, seed); } if (params.op !== "act") throw new DeskRefusal("invalid-operation", "Use discover, inspect, query or act."); const snapshot = changeReservation(state, params.action ?? "", params.id ?? "", params.packets ?? 0, params.expectedRevision ?? "", "agent"); signal?.throwIfAborted(); state = commit(ctx, snapshot, params.action ?? ""); if (ctx.hasUI) ctx.ui.notify("Agent updated a fictional session-local reservation. Real stock is unchanged.", "info"); return reply(state, `Recorded ${params.action} of ${params.packets} fictional packet(s).`, inspect(params.id ?? "", state)); } catch (error) { if (!(error instanceof DeskRefusal)) throw error; return { content: [{ type: "text", text: `Refused: ${error.message} Real stock is unchanged.` }], details: { ok: false, scope, code: error.code, revision: state?.revision }, }; } }, renderResult(result) { const text = result.content.filter(part => part.type === "text").map(part => part.text).join("\n"); // Rendering is optional presentation; content/details remain complete without a TUI. return new Text(text, 0, 0); }, }); pi.registerCommand("seeds", { description: "Fictional seed desk: query [family] | inspect | reserve/release | agent on/off", async handler(args, ctx) { try { const [verb = "query", value, count, extra] = args.trim().split(/\s+/).filter(Boolean); const initial = reconstruct(ctx); if (verb === "query" && !count) { const seeds = query(initial, value); announce(ctx, `${seeds.length ? seeds.map(seed => `${seed.id}: ${seed.available} available, ${seed.reserved} reserved`).join("\n") : "No seeds match."}\nFictional session branch only. Revision: ${initial.revision}`); return; } if (verb === "inspect" && value && !count) { const seed = inspect(value, initial); announce(ctx, `${seed.name}: ${seed.available} fictional packets available, ${seed.reserved} reserved. Revision: ${initial.revision}`); return; } const grant = verb === "agent" && (value === "on" || value === "off") && !count; const reservation = (verb === "reserve" || verb === "release") && value && count && !extra; if (!grant && !reservation) throw new DeskRefusal("invalid-command", "Use query [family], inspect , reserve/release , or agent on/off."); if (!ctx.hasUI) throw new DeskRefusal("ui-required", "Human changes require the interactive confirmation dialog; read-only commands still work without UI."); if (reservation) changeReservation(initial, verb, value ?? "", Number(count), initial.revision, "human"); const accepted = await ctx.ui.confirm("Fictional session-only change", grant ? `Turn agent reservations ${value} for this branch? No real stock changes.` : `${verb} ${count} fictional packet(s) of ${value}? No real stock changes.`); if (!accepted) { announce(ctx, "Cancelled. Nothing changed."); return; } // Re-read after the dialog: another action or tree navigation may have happened. const current = reconstruct(ctx); if (current.revision !== initial.revision) throw new DeskRefusal("stale-revision", "The branch changed during confirmation; try again."); const snapshot: Snapshot = grant ? { version: 1, quantities: current.quantities, agentEnabled: value === "on" } : changeReservation(current, verb, value ?? "", Number(count), initial.revision, "human"); const saved = commit(ctx, snapshot, grant ? `agent-${value}` : verb); announce(ctx, `Recorded ${grant ? `agent ${value}` : `${verb} ${count} packet(s)`} in this fictional session branch. Real stock is unchanged. Revision: ${saved.revision}`); } catch (error) { if (!(error instanceof DeskRefusal)) throw error; announce(ctx, error.message, true); } }, }); pi.on("session_start", (_event, ctx) => refresh(ctx)); pi.on("session_switch", (_event, ctx) => refresh(ctx)); pi.on("session_tree", (_event, ctx) => refresh(ctx)); pi.on("session_branch", (_event, ctx) => refresh(ctx)); pi.on("session_shutdown", (_event, ctx) => { if (ctx.hasUI) ctx.ui.setStatus("seed-desk", undefined); }); }