codeharness 0.32.1 → 0.32.3
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.
|
@@ -2895,7 +2895,7 @@ function generateDockerfileTemplate(projectDir, stackOrDetections) {
|
|
|
2895
2895
|
}
|
|
2896
2896
|
|
|
2897
2897
|
// src/modules/infra/init-project.ts
|
|
2898
|
-
var HARNESS_VERSION = true ? "0.32.
|
|
2898
|
+
var HARNESS_VERSION = true ? "0.32.3" : "0.0.0-dev";
|
|
2899
2899
|
function failResult(opts, error) {
|
|
2900
2900
|
return {
|
|
2901
2901
|
status: "fail",
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
validateDockerfile,
|
|
41
41
|
warn,
|
|
42
42
|
writeState
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-N57BYUXA.js";
|
|
44
44
|
|
|
45
45
|
// src/index.ts
|
|
46
46
|
import { Command } from "commander";
|
|
@@ -3360,12 +3360,17 @@ async function dispatchTaskWithResult(task, taskName, storyKey, definition, stat
|
|
|
3360
3360
|
let cwd;
|
|
3361
3361
|
let workspace = null;
|
|
3362
3362
|
if (task.source_access === false) {
|
|
3363
|
-
|
|
3364
|
-
|
|
3363
|
+
try {
|
|
3364
|
+
workspace = await createIsolatedWorkspace({ runId: config.runId, storyFiles: [] });
|
|
3365
|
+
cwd = workspace?.toDispatchOptions()?.cwd ?? projectDir;
|
|
3366
|
+
} catch {
|
|
3367
|
+
cwd = projectDir;
|
|
3368
|
+
}
|
|
3365
3369
|
} else {
|
|
3366
3370
|
cwd = projectDir;
|
|
3367
3371
|
}
|
|
3368
|
-
const
|
|
3372
|
+
const isEpicSentinel = storyKey.startsWith("__epic_") || storyKey === PER_RUN_SENTINEL;
|
|
3373
|
+
const basePrompt = customPrompt ?? (isEpicSentinel ? `Execute task "${taskName}" for the current run.` : `Implement story ${storyKey}`);
|
|
3369
3374
|
let prompt = buildPromptWithContractContext(basePrompt, previousOutputContract ?? null);
|
|
3370
3375
|
const coverageDedup = buildCoverageDeduplicationContext(
|
|
3371
3376
|
previousOutputContract ?? null,
|
|
@@ -3873,7 +3878,7 @@ async function executeWorkflow(config) {
|
|
|
3873
3878
|
}
|
|
3874
3879
|
if (state.phase === "error" || state.phase === "failed") {
|
|
3875
3880
|
const errorCount = state.tasks_completed.filter((t) => t.error).length;
|
|
3876
|
-
info(`Resuming from ${state.phase} state \u2014 ${errorCount} previous error(s), retrying failed tasks`);
|
|
3881
|
+
if (!config.onEvent) info(`Resuming from ${state.phase} state \u2014 ${errorCount} previous error(s), retrying failed tasks`);
|
|
3877
3882
|
}
|
|
3878
3883
|
state = {
|
|
3879
3884
|
...state,
|
|
@@ -3912,120 +3917,144 @@ async function executeWorkflow(config) {
|
|
|
3912
3917
|
for (const step of config.workflow.storyFlow) {
|
|
3913
3918
|
if (typeof step === "string") storyFlowTasks.add(step);
|
|
3914
3919
|
}
|
|
3920
|
+
const epicGroups = /* @__PURE__ */ new Map();
|
|
3921
|
+
for (const item of workItems) {
|
|
3922
|
+
const epicId = item.key.match(/^(\d+)-/)?.[1] ?? "unknown";
|
|
3923
|
+
if (!epicGroups.has(epicId)) epicGroups.set(epicId, []);
|
|
3924
|
+
epicGroups.get(epicId).push(item);
|
|
3925
|
+
}
|
|
3915
3926
|
let halted = false;
|
|
3916
3927
|
let lastOutputContract = null;
|
|
3917
3928
|
let accumulatedCostUsd = 0;
|
|
3918
|
-
for (const
|
|
3929
|
+
for (const [epicId, epicItems] of epicGroups) {
|
|
3919
3930
|
if (halted) break;
|
|
3920
3931
|
if (config.abortSignal?.aborted) {
|
|
3921
|
-
info("Execution interrupted \u2014 saving state");
|
|
3932
|
+
if (!config.onEvent) info("Execution interrupted \u2014 saving state");
|
|
3922
3933
|
state = { ...state, phase: "interrupted" };
|
|
3923
3934
|
writeWorkflowState(state, projectDir);
|
|
3924
3935
|
halted = true;
|
|
3925
3936
|
break;
|
|
3926
3937
|
}
|
|
3927
|
-
if (
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3938
|
+
if (config.onEvent) {
|
|
3939
|
+
config.onEvent({ type: "dispatch-start", taskName: "story_flow", storyKey: `__epic_${epicId}__` });
|
|
3940
|
+
} else {
|
|
3941
|
+
info(`[epic-${epicId}] Starting epic with ${epicItems.length} stories`);
|
|
3942
|
+
}
|
|
3943
|
+
for (const step of config.workflow.epicFlow) {
|
|
3944
|
+
if (halted) break;
|
|
3945
|
+
if (config.abortSignal?.aborted) {
|
|
3946
|
+
state = { ...state, phase: "interrupted" };
|
|
3947
|
+
writeWorkflowState(state, projectDir);
|
|
3948
|
+
halted = true;
|
|
3949
|
+
break;
|
|
3950
|
+
}
|
|
3951
|
+
if (step === "story_flow") {
|
|
3952
|
+
for (const item of epicItems) {
|
|
3939
3953
|
if (halted || config.abortSignal?.aborted) {
|
|
3954
|
+
if (config.abortSignal?.aborted) {
|
|
3955
|
+
state = { ...state, phase: "interrupted" };
|
|
3956
|
+
writeWorkflowState(state, projectDir);
|
|
3957
|
+
}
|
|
3940
3958
|
halted = true;
|
|
3941
3959
|
break;
|
|
3942
3960
|
}
|
|
3943
|
-
|
|
3944
|
-
const
|
|
3945
|
-
|
|
3946
|
-
if (!task2) {
|
|
3947
|
-
warn(`workflow-engine: task "${taskName2}" not found, skipping`);
|
|
3948
|
-
continue;
|
|
3949
|
-
}
|
|
3950
|
-
if (task2.agent === null) continue;
|
|
3951
|
-
const definition2 = config.agents[task2.agent];
|
|
3952
|
-
if (!definition2) {
|
|
3953
|
-
warn(`workflow-engine: agent "${task2.agent}" not found for "${taskName2}"`);
|
|
3954
|
-
continue;
|
|
3955
|
-
}
|
|
3956
|
-
if (isTaskCompleted(state, taskName2, item.key)) continue;
|
|
3957
|
-
try {
|
|
3958
|
-
const dr = await dispatchTaskWithResult(task2, taskName2, item.key, definition2, state, config, void 0, lastOutputContract ?? void 0);
|
|
3959
|
-
state = dr.updatedState;
|
|
3960
|
-
lastOutputContract = dr.contract;
|
|
3961
|
-
propagateVerifyFlags(taskName2, dr.contract, projectDir);
|
|
3962
|
-
accumulatedCostUsd += dr.contract?.cost_usd ?? 0;
|
|
3963
|
-
tasksCompleted++;
|
|
3964
|
-
} catch (err) {
|
|
3965
|
-
const engineError = handleDispatchError(err, taskName2, item.key);
|
|
3966
|
-
errors.push(engineError);
|
|
3967
|
-
if (config.onEvent) {
|
|
3968
|
-
config.onEvent({ type: "dispatch-error", taskName: taskName2, storyKey: item.key, error: { code: engineError.code, message: engineError.message } });
|
|
3969
|
-
} else {
|
|
3970
|
-
warn(`[${taskName2}] ${item.key} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
3971
|
-
}
|
|
3972
|
-
state = recordErrorInState(state, taskName2, item.key, engineError);
|
|
3973
|
-
writeWorkflowState(state, projectDir);
|
|
3974
|
-
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
3961
|
+
processedStories.add(item.key);
|
|
3962
|
+
for (const storyStep of config.workflow.storyFlow) {
|
|
3963
|
+
if (halted || config.abortSignal?.aborted) {
|
|
3975
3964
|
halted = true;
|
|
3965
|
+
break;
|
|
3966
|
+
}
|
|
3967
|
+
if (typeof storyStep !== "string") continue;
|
|
3968
|
+
const taskName2 = storyStep;
|
|
3969
|
+
const task2 = config.workflow.tasks[taskName2];
|
|
3970
|
+
if (!task2) {
|
|
3971
|
+
warn(`workflow-engine: task "${taskName2}" not found, skipping`);
|
|
3972
|
+
continue;
|
|
3973
|
+
}
|
|
3974
|
+
if (task2.agent === null) continue;
|
|
3975
|
+
const definition2 = config.agents[task2.agent];
|
|
3976
|
+
if (!definition2) {
|
|
3977
|
+
warn(`workflow-engine: agent "${task2.agent}" not found for "${taskName2}"`);
|
|
3978
|
+
continue;
|
|
3979
|
+
}
|
|
3980
|
+
if (isTaskCompleted(state, taskName2, item.key)) continue;
|
|
3981
|
+
try {
|
|
3982
|
+
const dr = await dispatchTaskWithResult(task2, taskName2, item.key, definition2, state, config, void 0, lastOutputContract ?? void 0);
|
|
3983
|
+
state = dr.updatedState;
|
|
3984
|
+
lastOutputContract = dr.contract;
|
|
3985
|
+
propagateVerifyFlags(taskName2, dr.contract, projectDir);
|
|
3986
|
+
accumulatedCostUsd += dr.contract?.cost_usd ?? 0;
|
|
3987
|
+
tasksCompleted++;
|
|
3988
|
+
} catch (err) {
|
|
3989
|
+
const engineError = handleDispatchError(err, taskName2, item.key);
|
|
3990
|
+
errors.push(engineError);
|
|
3991
|
+
if (config.onEvent) {
|
|
3992
|
+
config.onEvent({ type: "dispatch-error", taskName: taskName2, storyKey: item.key, error: { code: engineError.code, message: engineError.message } });
|
|
3993
|
+
} else {
|
|
3994
|
+
warn(`[${taskName2}] ${item.key} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
3995
|
+
}
|
|
3996
|
+
state = recordErrorInState(state, taskName2, item.key, engineError);
|
|
3997
|
+
writeWorkflowState(state, projectDir);
|
|
3998
|
+
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
3999
|
+
halted = true;
|
|
4000
|
+
}
|
|
4001
|
+
break;
|
|
3976
4002
|
}
|
|
3977
|
-
break;
|
|
3978
4003
|
}
|
|
3979
4004
|
}
|
|
4005
|
+
continue;
|
|
3980
4006
|
}
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
4007
|
+
if (isLoopBlock(step)) {
|
|
4008
|
+
const loopResult = await executeLoopBlock(step, state, config, epicItems, lastOutputContract, storyFlowTasks);
|
|
4009
|
+
state = loopResult.state;
|
|
4010
|
+
errors.push(...loopResult.errors);
|
|
4011
|
+
tasksCompleted += loopResult.tasksCompleted;
|
|
4012
|
+
lastOutputContract = loopResult.lastContract;
|
|
4013
|
+
for (const item of epicItems) processedStories.add(item.key);
|
|
4014
|
+
if (loopResult.halted || state.phase === "max-iterations" || state.phase === "circuit-breaker") {
|
|
4015
|
+
halted = true;
|
|
4016
|
+
}
|
|
4017
|
+
continue;
|
|
3992
4018
|
}
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
warn(`workflow-engine: task "${taskName}" not found, skipping`);
|
|
3999
|
-
continue;
|
|
4000
|
-
}
|
|
4001
|
-
if (task.agent === null) continue;
|
|
4002
|
-
const definition = config.agents[task.agent];
|
|
4003
|
-
if (!definition) {
|
|
4004
|
-
warn(`workflow-engine: agent "${task.agent}" not found for "${taskName}"`);
|
|
4005
|
-
continue;
|
|
4006
|
-
}
|
|
4007
|
-
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) continue;
|
|
4008
|
-
try {
|
|
4009
|
-
const dr = await dispatchTaskWithResult(task, taskName, PER_RUN_SENTINEL, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
4010
|
-
state = dr.updatedState;
|
|
4011
|
-
lastOutputContract = dr.contract;
|
|
4012
|
-
propagateVerifyFlags(taskName, dr.contract, projectDir);
|
|
4013
|
-
accumulatedCostUsd += dr.contract?.cost_usd ?? 0;
|
|
4014
|
-
tasksCompleted++;
|
|
4015
|
-
} catch (err) {
|
|
4016
|
-
const engineError = handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
4017
|
-
errors.push(engineError);
|
|
4018
|
-
if (config.onEvent) {
|
|
4019
|
-
config.onEvent({ type: "dispatch-error", taskName, storyKey: PER_RUN_SENTINEL, error: { code: engineError.code, message: engineError.message } });
|
|
4020
|
-
} else {
|
|
4021
|
-
warn(`[${taskName}] ${PER_RUN_SENTINEL} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
4019
|
+
const taskName = step;
|
|
4020
|
+
const task = config.workflow.tasks[taskName];
|
|
4021
|
+
if (!task) {
|
|
4022
|
+
warn(`workflow-engine: task "${taskName}" not found, skipping`);
|
|
4023
|
+
continue;
|
|
4022
4024
|
}
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
if (
|
|
4026
|
-
|
|
4025
|
+
if (task.agent === null) continue;
|
|
4026
|
+
const definition = config.agents[task.agent];
|
|
4027
|
+
if (!definition) {
|
|
4028
|
+
warn(`workflow-engine: agent "${task.agent}" not found for "${taskName}"`);
|
|
4029
|
+
continue;
|
|
4030
|
+
}
|
|
4031
|
+
const epicSentinel = `__epic_${epicId}__`;
|
|
4032
|
+
if (isTaskCompleted(state, taskName, epicSentinel)) continue;
|
|
4033
|
+
try {
|
|
4034
|
+
const dr = await dispatchTaskWithResult(task, taskName, epicSentinel, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
4035
|
+
state = dr.updatedState;
|
|
4036
|
+
lastOutputContract = dr.contract;
|
|
4037
|
+
propagateVerifyFlags(taskName, dr.contract, projectDir);
|
|
4038
|
+
accumulatedCostUsd += dr.contract?.cost_usd ?? 0;
|
|
4039
|
+
tasksCompleted++;
|
|
4040
|
+
} catch (err) {
|
|
4041
|
+
const engineError = handleDispatchError(err, taskName, epicSentinel);
|
|
4042
|
+
errors.push(engineError);
|
|
4043
|
+
if (config.onEvent) {
|
|
4044
|
+
config.onEvent({ type: "dispatch-error", taskName, storyKey: epicSentinel, error: { code: engineError.code, message: engineError.message } });
|
|
4045
|
+
} else {
|
|
4046
|
+
warn(`[${taskName}] epic-${epicId} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
4047
|
+
}
|
|
4048
|
+
state = recordErrorInState(state, taskName, epicSentinel, engineError);
|
|
4049
|
+
writeWorkflowState(state, projectDir);
|
|
4050
|
+
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
4051
|
+
halted = true;
|
|
4052
|
+
}
|
|
4027
4053
|
}
|
|
4028
4054
|
}
|
|
4055
|
+
if (!halted) {
|
|
4056
|
+
if (!config.onEvent) info(`[epic-${epicId}] Epic completed`);
|
|
4057
|
+
}
|
|
4029
4058
|
}
|
|
4030
4059
|
if (state.phase === "interrupted") {
|
|
4031
4060
|
} else if (errors.length === 0 && state.phase !== "max-iterations" && state.phase !== "circuit-breaker") {
|
|
@@ -5468,7 +5497,7 @@ function StoryContext({ entries }) {
|
|
|
5468
5497
|
if (entries.length === 0) return null;
|
|
5469
5498
|
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: entries.map((e, i) => {
|
|
5470
5499
|
if (e.role === "prev") return /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { color: "green", children: ` Prev: ${e.key} \u2713` }) }, i);
|
|
5471
|
-
if (e.role === "current") return /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: ` This: ${e.key}
|
|
5500
|
+
if (e.role === "current") return /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: ` This: ${e.key}` }) }, i);
|
|
5472
5501
|
return /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: ` Next: ${e.key}` }) }, i);
|
|
5473
5502
|
}) });
|
|
5474
5503
|
}
|
|
@@ -6214,17 +6243,17 @@ function registerRunCommand(program) {
|
|
|
6214
6243
|
total: counts.total,
|
|
6215
6244
|
totalCost: totalCostUsd
|
|
6216
6245
|
});
|
|
6217
|
-
if (
|
|
6218
|
-
const
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
storyEntries[
|
|
6225
|
-
renderer.updateStories([...storyEntries]);
|
|
6246
|
+
if (event.taskName === "verify" && event.storyKey.startsWith("__epic_")) {
|
|
6247
|
+
const epicId = event.storyKey.replace("__epic_", "").replace("__", "");
|
|
6248
|
+
for (let i = 0; i < storyEntries.length; i++) {
|
|
6249
|
+
const se = storyEntries[i];
|
|
6250
|
+
if (se.status === "in-progress" && se.key.startsWith(`${epicId}-`)) {
|
|
6251
|
+
storiesDone++;
|
|
6252
|
+
updateStoryStatus2(se.key, "done");
|
|
6253
|
+
storyEntries[i] = { ...se, status: "done" };
|
|
6226
6254
|
}
|
|
6227
6255
|
}
|
|
6256
|
+
renderer.updateStories([...storyEntries]);
|
|
6228
6257
|
}
|
|
6229
6258
|
}
|
|
6230
6259
|
if (event.type === "dispatch-error") {
|
|
@@ -11135,7 +11164,7 @@ function registerTeardownCommand(program) {
|
|
|
11135
11164
|
} else if (otlpMode === "remote-routed") {
|
|
11136
11165
|
if (!options.keepDocker) {
|
|
11137
11166
|
try {
|
|
11138
|
-
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-
|
|
11167
|
+
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-UY37PFPB.js");
|
|
11139
11168
|
stopCollectorOnly2();
|
|
11140
11169
|
result.docker.stopped = true;
|
|
11141
11170
|
if (!isJson) {
|
|
@@ -11167,7 +11196,7 @@ function registerTeardownCommand(program) {
|
|
|
11167
11196
|
info("Shared stack: kept running (other projects may use it)");
|
|
11168
11197
|
}
|
|
11169
11198
|
} else if (isLegacyStack) {
|
|
11170
|
-
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-
|
|
11199
|
+
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-UY37PFPB.js");
|
|
11171
11200
|
let stackRunning = false;
|
|
11172
11201
|
try {
|
|
11173
11202
|
stackRunning = isStackRunning2(composeFile);
|
|
@@ -14154,7 +14183,7 @@ function registerDriversCommand(program) {
|
|
|
14154
14183
|
}
|
|
14155
14184
|
|
|
14156
14185
|
// src/index.ts
|
|
14157
|
-
var VERSION = true ? "0.32.
|
|
14186
|
+
var VERSION = true ? "0.32.3" : "0.0.0-dev";
|
|
14158
14187
|
function createProgram() {
|
|
14159
14188
|
const program = new Command();
|
|
14160
14189
|
program.name("codeharness").description("Makes autonomous coding agents produce software that actually works").version(VERSION).option("--json", "Output in machine-readable JSON format");
|