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.
44 lines
1.5 KiB
TypeScript
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;
|