@workermill/agent 0.6.1 → 0.7.0
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.
- package/dist/config.d.ts +2 -0
- package/dist/config.js +2 -0
- package/dist/plan-validator.d.ts +8 -0
- package/dist/plan-validator.js +25 -3
- package/dist/planner.js +17 -4
- package/dist/poller.js +5 -1
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface AgentConfig {
|
|
|
17
17
|
gitlabToken: string;
|
|
18
18
|
workerImage: string;
|
|
19
19
|
teamPlanningEnabled: boolean;
|
|
20
|
+
analystModel: string;
|
|
20
21
|
}
|
|
21
22
|
export interface FileConfig {
|
|
22
23
|
apiUrl: string;
|
|
@@ -32,6 +33,7 @@ export interface FileConfig {
|
|
|
32
33
|
};
|
|
33
34
|
workerImage: string;
|
|
34
35
|
teamPlanningEnabled?: boolean;
|
|
36
|
+
analystModel?: string;
|
|
35
37
|
setupCompletedAt: string;
|
|
36
38
|
}
|
|
37
39
|
export declare function getConfigDir(): string;
|
package/dist/config.js
CHANGED
|
@@ -76,6 +76,7 @@ export function loadConfigFromFile() {
|
|
|
76
76
|
gitlabToken: fc.tokens?.gitlab || "",
|
|
77
77
|
workerImage,
|
|
78
78
|
teamPlanningEnabled: fc.teamPlanningEnabled ?? true,
|
|
79
|
+
analystModel: fc.analystModel || "sonnet",
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
/**
|
|
@@ -121,6 +122,7 @@ export function loadConfig() {
|
|
|
121
122
|
gitlabToken: process.env.GITLAB_TOKEN || "",
|
|
122
123
|
workerImage: process.env.WORKER_IMAGE || "workermill-worker:local",
|
|
123
124
|
teamPlanningEnabled: process.env.TEAM_PLANNING_ENABLED !== "false",
|
|
125
|
+
analystModel: process.env.ANALYST_MODEL || "sonnet",
|
|
124
126
|
};
|
|
125
127
|
}
|
|
126
128
|
/**
|
package/dist/plan-validator.d.ts
CHANGED
|
@@ -53,6 +53,14 @@ export declare function applyFileCap(plan: ExecutionPlan): {
|
|
|
53
53
|
truncatedCount: number;
|
|
54
54
|
details: string[];
|
|
55
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Apply story cap to the plan. Truncates stories beyond maxStories.
|
|
58
|
+
* Returns details about dropped stories for logging.
|
|
59
|
+
*/
|
|
60
|
+
export declare function applyStoryCap(plan: ExecutionPlan, maxStories: number): {
|
|
61
|
+
droppedCount: number;
|
|
62
|
+
details: string[];
|
|
63
|
+
};
|
|
56
64
|
/**
|
|
57
65
|
* Re-serialize plan as a JSON code block for posting to the API.
|
|
58
66
|
* The server-side parseExecutionPlan() expects ```json ... ``` blocks.
|
package/dist/plan-validator.js
CHANGED
|
@@ -59,6 +59,28 @@ export function applyFileCap(plan) {
|
|
|
59
59
|
return { truncatedCount, details };
|
|
60
60
|
}
|
|
61
61
|
// ============================================================================
|
|
62
|
+
// STORY CAP
|
|
63
|
+
// ============================================================================
|
|
64
|
+
/**
|
|
65
|
+
* Apply story cap to the plan. Truncates stories beyond maxStories.
|
|
66
|
+
* Returns details about dropped stories for logging.
|
|
67
|
+
*/
|
|
68
|
+
export function applyStoryCap(plan, maxStories) {
|
|
69
|
+
if (plan.stories.length <= maxStories) {
|
|
70
|
+
return { droppedCount: 0, details: [] };
|
|
71
|
+
}
|
|
72
|
+
const droppedCount = plan.stories.length - maxStories;
|
|
73
|
+
const dropped = plan.stories.slice(maxStories);
|
|
74
|
+
const details = dropped.map((s) => `${s.id}: "${s.title}" (${s.persona})`);
|
|
75
|
+
plan.stories = plan.stories.slice(0, maxStories);
|
|
76
|
+
// Fix dependencies that reference dropped stories
|
|
77
|
+
const validIds = new Set(plan.stories.map((s) => s.id));
|
|
78
|
+
for (const story of plan.stories) {
|
|
79
|
+
story.dependencies = story.dependencies.filter((dep) => validIds.has(dep));
|
|
80
|
+
}
|
|
81
|
+
return { droppedCount, details };
|
|
82
|
+
}
|
|
83
|
+
// ============================================================================
|
|
62
84
|
// PLAN SERIALIZATION
|
|
63
85
|
// ============================================================================
|
|
64
86
|
/**
|
|
@@ -183,8 +205,8 @@ export function runCriticCli(claudePath, model, prompt, env) {
|
|
|
183
205
|
});
|
|
184
206
|
const timeout = setTimeout(() => {
|
|
185
207
|
proc.kill("SIGTERM");
|
|
186
|
-
reject(new Error("Critic CLI timed out after
|
|
187
|
-
},
|
|
208
|
+
reject(new Error("Critic CLI timed out after 20 minutes"));
|
|
209
|
+
}, 1_200_000);
|
|
188
210
|
proc.on("exit", (code) => {
|
|
189
211
|
clearTimeout(timeout);
|
|
190
212
|
if (code !== 0) {
|
|
@@ -262,7 +284,7 @@ export async function runCriticValidation(claudePath, model, prd, plan, env, tas
|
|
|
262
284
|
if (!providerApiKey) {
|
|
263
285
|
throw new Error(`No API key for critic provider "${effectiveProvider}"`);
|
|
264
286
|
}
|
|
265
|
-
rawCriticOutput = await generateText(effectiveProvider, model, criticPrompt, providerApiKey, { maxTokens: 4096, temperature: 0.3, timeoutMs:
|
|
287
|
+
rawCriticOutput = await generateText(effectiveProvider, model, criticPrompt, providerApiKey, { maxTokens: 4096, temperature: 0.3, timeoutMs: 1_200_000 });
|
|
266
288
|
}
|
|
267
289
|
const result = parseCriticResponse(rawCriticOutput);
|
|
268
290
|
const statusIcon = result.score >= AUTO_APPROVAL_THRESHOLD
|
package/dist/planner.js
CHANGED
|
@@ -18,7 +18,7 @@ import chalk from "chalk";
|
|
|
18
18
|
import { spawn, execSync } from "child_process";
|
|
19
19
|
import { findClaudePath } from "./config.js";
|
|
20
20
|
import { api } from "./api.js";
|
|
21
|
-
import { parseExecutionPlan, applyFileCap, serializePlan, runCriticValidation, formatCriticFeedback, AUTO_APPROVAL_THRESHOLD, } from "./plan-validator.js";
|
|
21
|
+
import { parseExecutionPlan, applyFileCap, applyStoryCap, serializePlan, runCriticValidation, formatCriticFeedback, AUTO_APPROVAL_THRESHOLD, } from "./plan-validator.js";
|
|
22
22
|
import { generateText } from "./providers.js";
|
|
23
23
|
/** Max Planner-Critic iterations before giving up */
|
|
24
24
|
const MAX_ITERATIONS = 3;
|
|
@@ -546,7 +546,8 @@ export async function planTask(task, config, credentials) {
|
|
|
546
546
|
const promptResponse = await api.get("/api/agent/planning-prompt", {
|
|
547
547
|
params: { taskId: task.id },
|
|
548
548
|
});
|
|
549
|
-
const { prompt: basePrompt, model, provider: planningProvider } = promptResponse.data;
|
|
549
|
+
const { prompt: basePrompt, model, provider: planningProvider, maxStories: apiMaxStories } = promptResponse.data;
|
|
550
|
+
const maxStories = typeof apiMaxStories === "number" ? apiMaxStories : 8;
|
|
550
551
|
const cliModel = model || "sonnet";
|
|
551
552
|
const provider = (planningProvider || "anthropic");
|
|
552
553
|
const isAnthropicPlanning = provider === "anthropic";
|
|
@@ -577,7 +578,9 @@ export async function planTask(task, config, credentials) {
|
|
|
577
578
|
console.log(`${ts()} ${taskLabel} ${chalk.yellow("⚠")} No SCM token for ${scmProvider}, skipping team planning`);
|
|
578
579
|
}
|
|
579
580
|
if (repoPath) {
|
|
580
|
-
const
|
|
581
|
+
const analystModel = config.analystModel || "sonnet";
|
|
582
|
+
console.log(`${ts()} ${taskLabel} Analysts using model: ${chalk.yellow(analystModel)} (planner: ${chalk.yellow(cliModel)})`);
|
|
583
|
+
const analysisResult = await runTeamAnalysis(task, basePrompt, claudePath, analystModel, cleanEnv, repoPath, task.id, startTime);
|
|
581
584
|
if (analysisResult) {
|
|
582
585
|
enhancedBasePrompt = analysisResult;
|
|
583
586
|
}
|
|
@@ -656,7 +659,17 @@ export async function planTask(task, config, credentials) {
|
|
|
656
659
|
console.log(`${ts()} ${taskLabel} ${chalk.dim(detail)}`);
|
|
657
660
|
}
|
|
658
661
|
}
|
|
659
|
-
|
|
662
|
+
// 2c2. Apply story cap (max stories from org calibration)
|
|
663
|
+
const { droppedCount: storyDropCount, details: storyDropDetails } = applyStoryCap(plan, maxStories);
|
|
664
|
+
if (storyDropCount > 0) {
|
|
665
|
+
const msg = `${PREFIX} Story cap applied: ${storyDropCount} stories dropped (max ${maxStories})`;
|
|
666
|
+
console.log(`${ts()} ${taskLabel} ${chalk.yellow("⚠")} ${msg}`);
|
|
667
|
+
await postLog(task.id, msg);
|
|
668
|
+
for (const detail of storyDropDetails) {
|
|
669
|
+
console.log(`${ts()} ${taskLabel} ${chalk.dim(detail)}`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
console.log(`${ts()} ${taskLabel} Plan: ${chalk.bold(plan.stories.length)} stories (max ${maxStories})`);
|
|
660
673
|
await postLog(task.id, `${PREFIX} Plan generated: ${plan.stories.length} stories (${formatElapsed(elapsed)}). Running critic validation...`);
|
|
661
674
|
// 2d. Run critic validation
|
|
662
675
|
const criticResult = await runCriticValidation(claudePath, cliModel, prd, plan, cleanEnv, taskLabel, provider, providerApiKey);
|
package/dist/poller.js
CHANGED
|
@@ -250,7 +250,11 @@ export function startPolling(config) {
|
|
|
250
250
|
*/
|
|
251
251
|
export function startHeartbeat(config) {
|
|
252
252
|
setInterval(async () => {
|
|
253
|
-
|
|
253
|
+
// Include BOTH running containers AND tasks being planned/managed
|
|
254
|
+
const containerTaskIds = getActiveTaskIds();
|
|
255
|
+
const planningTaskIds = Array.from(planningInProgress);
|
|
256
|
+
const managerTaskIds = Array.from(managerInProgress);
|
|
257
|
+
const activeTaskIds = [...containerTaskIds, ...planningTaskIds, ...managerTaskIds];
|
|
254
258
|
try {
|
|
255
259
|
const response = await api.post("/api/agent/heartbeat", {
|
|
256
260
|
agentId: config.agentId,
|