import { type Router as ExpressRouter } from "express"; /** What actually performed the move — reported back so a caller (and the test) * can see which branch ran. */ export type MoveKind = "git" | "rename"; export interface AgentMoveResponse { ok: true; /** False for the silent no-op: `to` resolved to `from`, nothing happened. */ moved: boolean; from: string; to: string; kind: MoveKind | null; } export interface AgentMoveDeps { /** * The registered agent at this absolute path, or null. Only a registered * agent may be moved: the rail can only drag rows it renders, and the rows * it renders come from this same cache. */ resolveAgent: (agentPath: string) => { name: string; path: string; } | null; /** * Every directory an agent may be dropped INTO: the project roots the studio * knows about, plus the branching directories the Project axis renders * between a root and an agent. `moveTargetDirs` below derives exactly that * set from the two things the server already holds, so "a directory the rail * can show" and "a directory this route will move into" are one list. */ listMoveTargetDirs: () => string[] | Promise; /** * Applied AFTER a successful move, before the response. The integrator's job: * remap live session cwds that sat inside the moved tree, prune the registry * of the path that no longer exists, rescan the destination, and broadcast * `workflows.changed` so the rail re-derives the tree from the new path. */ onMoved: (from: string, to: string) => Promise; } /** Whether `child` IS `parent` or sits beneath it, on segment boundaries — so * `…/ads-v2` is never read as a child of `…/ads`. Node-side twin of the * browser's `paths.isWithinDir`; `path.relative` does the normalizing. */ export declare function isWithinDir(parent: string, child: string): boolean; /** * Every directory the Project axis can offer as a drop target, derived from the * only two things the server holds: the roots the studio knows about and the * agents the registry has found. * * This mirrors `web/src/lib/project-tree.ts` exactly, because it has to. That * module tries each root, keeps the agents beneath it, and renders the path * segments between them as directory rows; a row that exists there is a row a * drag can land on, and nothing else is. So: each root, plus each directory * between a root and an agent inside it. * * KNOWN GAP, deliberately: the rail also shows `pendingCwds` — a folder chosen * for an agent that is still being created — and the server has no such list. * Dropping into a project that exists only mid-creation is refused with the * "directory the studio doesn't show" message until the folder lands in * `recentDirs`, which is the next thing that happens to it. Refusing a rare * drop is the right side to fail on for a route that renames user code. */ export declare function moveTargetDirs(roots: readonly string[], agentPaths: readonly string[]): string[]; /** * The path `p` becomes once `from` has moved to `to` — unchanged when `p` is * not inside `from`. * * Everything under the moved directory travels with it, so a nested agent and a * SESSION whose cwd sat inside the tree are remapped by the same rule. Without * this a session keeps pointing at a directory that no longer exists. */ export declare function remapUnder(p: string, from: string, to: string): string; /** * Rewrites every live session that sat inside the moved tree, IN PLACE, and * returns how many changed. * * A function rather than a loop at the wiring site because this is the criterion * "sessions whose cwd sat inside the moved directory follow it", and a rule that * lives inside a route's callback cannot be tested. On disk the session's own * process cwd follows the directory automatically (a cwd is an inode, not a * string), so what has to be corrected is the RECORD — the path the studio shows, * files under, and reboots the session at. Left stale, the session's project row * points at a directory that no longer exists. * * `boundWorkflowPath` travels the same way: the binding names the agent's * directory, and the agent just moved. * * In place, because the session manager owns these objects and hands out live * references — `setBoundWorkflowPath` mutates them the same way. */ export declare function remapSessions(sessions: Array<{ cwd: string; boundWorkflowPath?: string | null; }>, from: string, to: string): number; /** * Whether git TRACKS anything inside `dir`. * * `ls-files` rather than `rev-parse --is-inside-work-tree`: a directory can sit * inside a repo and be entirely gitignored (a scratch agent under a checkout's * `tmp/`), and `git mv` refuses those. "Inside a repo" is the wrong question; * "does git know about these files" is the one that predicts whether `git mv` * can do the job. No repo at all, or git not installed, reads as untracked. */ export declare function isGitTracked(dir: string): Promise; /** * Moves the directory, preferring the tracked path. * * A `git mv` failure falls THROUGH to the plain rename rather than failing the * request: the commonest reason is a destination outside this work tree (moving * an agent out of a checkout is a legitimate move), and the untracked branch * would have done exactly this anyway. What must never happen is a move that * half-succeeds, and neither branch can do that — both are single operations. */ export declare function performMove(from: string, to: string): Promise; /** * The route's OWN refusal, from the filesystem rather than from a path list — * a message when the move must not happen, null when it may. * * Ordered so the most specific truth wins: a destination inside the source is * the move that destroys the thing being moved (`mv a a/b` relocates the * destination along with the source and leaves nothing behind), and it is worth * saying so rather than reporting whatever `stat` happens to find there. */ export declare function refuseMoveOnDisk(from: string, to: string): Promise; /** * POST /api/agents/move { from, to } -> AgentMoveResponse * * 400 — a malformed or unregistered `from`, or a `to` that asks for a rename. * 409 — a refusal, with the reason in `error` so the rail can show it verbatim: * a destination inside the source, a destination the studio doesn't show, or * anything `stat` finds already sitting there. 200 with `moved: false` — `to` * resolved to `from`: the user let go somewhere harmless, and the SILENT no-op * is a success, not a complaint. */ export declare function createAgentMoveRouter(deps: AgentMoveDeps): ExpressRouter; //# sourceMappingURL=agent-move.d.ts.map