Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions connectors/delegate/editor/editor_tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,20 @@ export function register(server) {

server.tool(
"delegate_editor",
"TRIGGERS: general-purpose repo edits on a feature branch -- docs, config, backend code, tests, etc. -- that aren't frontend " +
"HTML/CSS/JSX/Vue (use delegate_designer for those) and aren't a read-only investigation (use delegate_agent for those).\n" +
"IS: WRITE TOOL, bounded agentic loop (default " + EDITOR_DEFAULT_STEPS + " steps, hard cap " + EDITOR_HARD_MAX_STEPS + ") with three tools of its own (read_file/write_file/validate) -- commits to the repo in the same call. Returns the agent's own final text summary plus which files it wrote, not the generated code inline.\n" +
"IS: WRITE TOOL, bounded agentic loop (default " + EDITOR_DEFAULT_STEPS + " steps, hard cap " + EDITOR_HARD_MAX_STEPS + ") with three tools of its own (read_file/write_file/validate) -- commits to the repo in the same call. Returns the agent's own final text summary plus which files it wrote, not the generated code inline.\n" +
"SCOPE, ENFORCED AT THE TOOL LAYER (not just prompt instructions): reads/writes fenced to extensions " + scopeSummary() + ". " +
"A hard deny list (independent of the allowlist) always blocks .github/workflows/**, connectors/security.js, and GitHub-App auth files, and blocks package.json's scripts/dependencies fields specifically -- regardless of extension. " +
"This run may touch at most " + EDITOR_MAX_FILES_PER_RUN + " distinct file(s), and write any single file at most " + EDITOR_MAX_WRITES_PER_FILE + " time(s).\n" +
"PREREQUISITE: `branch` MUST already exist and MUST NOT be the repo's default branch -- checked live against the GitHub API before any tool call, never trusted from the argument alone. No branch yet -> call create_branch first.\n" +
"CANNOT open, approve, or merge pull requests -- this tool has no create_pull_request/merge_pull_request in its own function set, structurally, not just by convention. A human reviews the branch's diff afterward; this tool only gets it ready for that review.\n" +
"RESUME: failed/partial run -> response includes resume_run_id -> pass back to continue from the last completed step instead of restarting.\n" +
"RESUME: a run that stopped partway (e.g. hit its step cap) returns a resume_run_id -> pass it back to continue from the last completed step. A run that failed permanently (error) cannot be resumed this way.\n" +
"ASYNC: a fresh call may return a run_id immediately while the run keeps stepping server-side in the background. If still running, sleep 60 seconds before polling again if you have nothing else to do, then call again with the same resume_run_id. Only a resume_run_id call returns the final result.\n" +
"POLLING vs PUSHING (async/QStash mode only -- see below): to just check progress on a resume_run_id, call with NO max_steps -- this is always read-only, even in the rare case where the background worker chain has stalled (you'll get a stalled status instead of a stored answer, never a silent extra step or write). Only pass max_steps when you actually want to advance the run further right now (raising the ceiling on a resumed run, or nudging a stalled one forward) -- an explicit max_steps is what authorizes real work (including further commits) to happen on that call. This distinction only applies when the async worker is configured; in synchronous mode there is no separate poll state at all -- every resume_run_id call continues the loop immediately regardless of max_steps, same as always.",
"POLLING vs PUSHING (async/QStash mode only): to just check progress on a resume_run_id, call with NO max_steps. Only pass max_steps when you actually want to advance a run further that ran out of steps -- not a permanently failed run.",
{
owner: z.string().optional().describe(`Repository owner. Defaults to "${DEFAULT_OWNER}" if omitted.`),
repo: z.string().optional().describe("Repository name. Not needed when resuming (resume_run_id carries it)."),
branch: z.string().optional().describe("Branch to work on. Must already exist and MUST NOT be the repo's default branch. Not needed when resuming."),
task: z.string().optional().describe("What to change, described with enough detail for the agent to act without asking anything back -- it can't. Ignored when resume_run_id resolves to a live checkpoint (the original task from that run is reused). Optional ONLY when resume_run_id is given and its checkpoint is still live; required otherwise."),
max_steps: z.number().optional().describe(`Max agent steps before being forced to answer (default ${EDITOR_DEFAULT_STEPS}, hard cap ${EDITOR_HARD_MAX_STEPS} regardless of this value). LEAVE UNSET on a fresh call -- there's no reliable way to size this upfront from the task description alone, and an undersized guess just causes the run to hit the cap mid-task before it ever gets to write. Only pass an explicit value when RESUMING a run that already came back reporting it hit its step cap (the failed run's own reported step count, plus the transcript, is real evidence for how many more steps are actually needed -- use that, not a fresh guess). On a resumed run this is the new ceiling, not additional steps on top of what's already done.`),
task: z.string().optional().describe("What to change, described with enough detail for the agent to act without asking anything back -- it can't. The agent has no directory-listing or search capability, so include exact file paths for anything it needs to read or write -- it cannot discover them on its own. Ignored when resume_run_id resolves to a live checkpoint (the original task from that run is reused). Optional ONLY when resume_run_id is given and its checkpoint is still live; required otherwise."),
max_steps: z.number().optional().describe(`Max agent steps before being forced to answer (default ${EDITOR_DEFAULT_STEPS}, hard cap ${EDITOR_HARD_MAX_STEPS} regardless of this value). Leave unset on a fresh call. Only pass an explicit value when resuming a run that already stopped because it hit its step cap. On a resumed run, this is the new ceiling -- not additional steps on top of what's already done.`),
resume_run_id: z.string().optional().describe("A runId returned from a previous failed/partial delegate_editor call. If its checkpoint is still live (1 hour TTL), continues that run's conversation instead of starting fresh."),
show_transcript: z.boolean().optional().describe("Include the full step-by-step tool-call transcript in the response, even on a successful run (default: false). On a failed/partial run the transcript is always shown regardless of this flag."),
provider: z.enum(["gemini"]).optional()
Expand Down
Loading