agent-dealer 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { planGateDecision } from "@agent-dealer/shared";
2
- import { buildPlanRevisePrompt } from "../runners/prompts.js";
2
+ import { buildPlanEditedReplanPrompt, buildPlanFeedbackPrompt, buildPlanRevisePrompt, } from "../runners/prompts.js";
3
3
  import { addArtifact, countByStatus, getLatestArtifact, getRun, listArtifacts, listRuns, markPlanTriageConsumed, patchRunPhaseBudget, transitionRun, updateRunFields, } from "../repository/runs.js";
4
4
  import { runAgent } from "../runners/claude.js";
5
5
  import { persistRunOutput, seedDeliverableFromParent } from "../runners/persist.js";
@@ -420,15 +420,38 @@ function scheduleRevisePlan(run, triage, answers) {
420
420
  });
421
421
  }
422
422
  /** Fire-and-forget: human requested a new agent plan (replaces draft). */
423
- export function scheduleRedraft(run) {
423
+ export function scheduleRedraft(run, opts) {
424
424
  if (activePlanDrafts.has(run.id))
425
425
  return false;
426
426
  if (run.status !== "plan_pending" && run.status !== "queued")
427
427
  return false;
428
428
  if (!run.runtime)
429
429
  return false;
430
+ const feedback = opts?.feedback?.trim();
431
+ const editedMarkdown = opts?.editedMarkdown?.trim();
432
+ let revise;
433
+ if (editedMarkdown) {
434
+ const draft = getLatestArtifact(run.id, "draft_plan");
435
+ const sessionId = draft?.contentJson
436
+ ? JSON.parse(draft.contentJson).sessionId
437
+ : undefined;
438
+ revise = {
439
+ resumeSessionId: sessionId,
440
+ prompt: buildPlanEditedReplanPrompt(run, editedMarkdown),
441
+ };
442
+ }
443
+ else if (feedback) {
444
+ const draft = getLatestArtifact(run.id, "draft_plan");
445
+ const sessionId = draft?.contentJson
446
+ ? JSON.parse(draft.contentJson).sessionId
447
+ : undefined;
448
+ revise = {
449
+ resumeSessionId: sessionId,
450
+ prompt: buildPlanFeedbackPrompt(run, feedback),
451
+ };
452
+ }
430
453
  activePlanDrafts.add(run.id);
431
- void draftPlan(run, { replace: true })
454
+ void draftPlan(run, { replace: true, ...(revise ? { revise } : {}) })
432
455
  .catch((e) => console.error("[plan-redraft]", run.id, e))
433
456
  .finally(() => {
434
457
  activePlanDrafts.delete(run.id);
@@ -261,7 +261,10 @@ export async function registerRoutes(app) {
261
261
  }
262
262
  try {
263
263
  assertAgentConfigured(getRun(id));
264
- const scheduled = scheduleRedraft(getRun(id));
264
+ const scheduled = scheduleRedraft(getRun(id), {
265
+ feedback: input.feedback,
266
+ editedMarkdown: input.editedMarkdown,
267
+ });
265
268
  if (!scheduled) {
266
269
  return reply.status(409).send({ error: "Plan draft already in progress" });
267
270
  }
@@ -268,6 +268,32 @@ function planDelegationSections(run) {
268
268
  return [];
269
269
  }
270
270
  }
271
+ export function buildPlanFeedbackPrompt(run, feedback) {
272
+ return [
273
+ "The human reviewed your plan and wants a revision. Revise the plan accordingly — do not execute anything.",
274
+ "",
275
+ "## Task",
276
+ taskText(run),
277
+ "",
278
+ "## Human feedback",
279
+ feedback.trim(),
280
+ "",
281
+ planTriageContractSection(run.taskCategory),
282
+ ].join("\n");
283
+ }
284
+ export function buildPlanEditedReplanPrompt(run, editedMarkdown) {
285
+ return [
286
+ "The human edited the plan directly. Start from their edits and produce a refined plan — do not execute anything.",
287
+ "",
288
+ "## Task",
289
+ taskText(run),
290
+ "",
291
+ "## Human-edited plan",
292
+ editedMarkdown.trim(),
293
+ "",
294
+ planTriageContractSection(run.taskCategory),
295
+ ].join("\n");
296
+ }
271
297
  export function buildPlanRevisePrompt(run, questions, answers) {
272
298
  const qa = answers.map((a) => {
273
299
  const q = questions.find((x) => x.id === a.questionId);
@@ -9,7 +9,7 @@ const { migrate } = await import("../db/index.js");
9
9
  const { BUILTIN_AGENT_CLAUDE_ID } = await import("@agent-dealer/shared");
10
10
  const { updateAgent } = await import("../repository/agents.js");
11
11
  const { addArtifact, createRun } = await import("../repository/runs.js");
12
- const { buildExecutionPrompt, buildPlanPrompt, buildPlanRevisePrompt, buildQaPrompt } = await import("./prompts.js");
12
+ const { buildExecutionPrompt, buildPlanPrompt, buildPlanEditedReplanPrompt, buildPlanFeedbackPrompt, buildPlanRevisePrompt, buildQaPrompt } = await import("./prompts.js");
13
13
  const QUESTIONS = [
14
14
  {
15
15
  id: "q1",
@@ -75,6 +75,18 @@ test("execution prompt omits answers section when none exist", () => {
75
75
  addArtifact(run.id, "approved_plan", { markdown: "# Plan" }, "human");
76
76
  assert.doesNotMatch(buildExecutionPrompt(run), /Human answers to plan questions/);
77
77
  });
78
+ test("feedback replan prompt includes human comments and contract", () => {
79
+ const run = makeRun();
80
+ const prompt = buildPlanFeedbackPrompt(run, "Add integration tests and skip the migration step.");
81
+ assert.match(prompt, /Add integration tests/);
82
+ assert.match(prompt, /"verdict"/);
83
+ });
84
+ test("edited replan prompt includes human-edited markdown", () => {
85
+ const run = makeRun();
86
+ const prompt = buildPlanEditedReplanPrompt(run, "# Revised plan\n\n1. Ship feature");
87
+ assert.match(prompt, /Revised plan/);
88
+ assert.match(prompt, /Ship feature/);
89
+ });
78
90
  test("revise prompt pairs each answer with its question and re-states the contract", () => {
79
91
  const run = makeRun();
80
92
  const prompt = buildPlanRevisePrompt(run, QUESTIONS, [{ questionId: "q1", freeText: "Use flat files instead" }]);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-dealer/server",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "files": [