Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/src/agent/detail/instructions.ts
iris b5d2e310ba swarm-ui: an editor for an agent's AGENTS.md that saves as a diff
The instructions row of the agent detail panel gets an edit button that
opens the agent's AGENTS.md, as the harness last published it, in a text
box. Save sends a unified diff against the revision that was loaded; when
the agent changed the file meanwhile the editor keeps the operator's text,
shows the agent's version and offers to save over it or take it. An unsaved
edit survives closing the dialog.

The diff is built in the browser (LCS over lines, three lines of context,
git's no-newline marker) and was checked against git apply on 600 random
before/after pairs.
2026-10-18 13:17:52 +02:00

44 lines
1.5 KiB
TypeScript

// What the AGENTS.md editor sends and reads. The routes are
// `GET` and `PUT /api/agents/{name}/agents-md` (swarm-controller-api's
// `AgentsMdView`, `AgentsMdEdit` and `AgentsMdSaved`).
import { unifiedDiff } from "./unified-diff.js";
/** What `GET` answers: the text as the agent last published it, and its id. */
export interface Instructions {
content: string;
/** The git blob id of `content`, which an edit names as its base. */
revision: string;
}
/** The body of a `PUT`. */
export interface InstructionsEdit {
base_revision: string;
diff: string;
}
export function instructionsUrl(agent: string): string {
return `/api/agents/${encodeURIComponent(agent)}/agents-md`;
}
/** An answer of the wrong shape is no answer: the editor shows an error. */
export function parseInstructions(json: unknown): Instructions | null {
if (typeof json !== "object" || json === null) return null;
const { content, revision } = json as Record<string, unknown>;
if (typeof content !== "string" || typeof revision !== "string") return null;
return { content, revision };
}
/**
* The `PUT` body that turns `base` into `draft`, or `null` when they are the
* same text and there is nothing to save.
*/
export function editBody(
base: Instructions,
draft: string,
): InstructionsEdit | null {
const diff = unifiedDiff(base.content, draft);
return diff === "" ? null : { base_revision: base.revision, diff };
}
/** The status the swarm answers a `PUT` with when the file moved on. */
export const STALE_STATUS = 409;