codeharness 0.29.2 → 0.29.4
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.29.
|
|
2898
|
+
var HARNESS_VERSION = true ? "0.29.4" : "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-KVKEUNEB.js";
|
|
44
44
|
|
|
45
45
|
// src/index.ts
|
|
46
46
|
import { Command } from "commander";
|
|
@@ -3884,6 +3884,13 @@ async function executeWorkflow(config) {
|
|
|
3884
3884
|
let accumulatedCostUsd = 0;
|
|
3885
3885
|
for (const step of config.workflow.storyFlow) {
|
|
3886
3886
|
if (halted) break;
|
|
3887
|
+
if (config.abortSignal?.aborted) {
|
|
3888
|
+
info("Execution interrupted \u2014 saving state");
|
|
3889
|
+
state = { ...state, phase: "interrupted" };
|
|
3890
|
+
writeWorkflowState(state, projectDir);
|
|
3891
|
+
halted = true;
|
|
3892
|
+
break;
|
|
3893
|
+
}
|
|
3887
3894
|
if (isLoopBlock(step)) {
|
|
3888
3895
|
const loopResult = await executeLoopBlock(step, state, config, workItems, lastOutputContract);
|
|
3889
3896
|
state = loopResult.state;
|
|
@@ -4004,6 +4011,10 @@ async function executeWorkflow(config) {
|
|
|
4004
4011
|
}
|
|
4005
4012
|
} else {
|
|
4006
4013
|
for (const item of workItems) {
|
|
4014
|
+
if (config.abortSignal?.aborted) {
|
|
4015
|
+
halted = true;
|
|
4016
|
+
break;
|
|
4017
|
+
}
|
|
4007
4018
|
processedStories.add(item.key);
|
|
4008
4019
|
if (isTaskCompleted(state, taskName, item.key)) {
|
|
4009
4020
|
warn(`workflow-engine: skipping completed task ${taskName} for ${item.key}`);
|
|
@@ -4044,13 +4055,14 @@ async function executeWorkflow(config) {
|
|
|
4044
4055
|
}
|
|
4045
4056
|
}
|
|
4046
4057
|
}
|
|
4047
|
-
if (
|
|
4058
|
+
if (state.phase === "interrupted") {
|
|
4059
|
+
} else if (errors.length === 0 && state.phase !== "max-iterations" && state.phase !== "circuit-breaker") {
|
|
4048
4060
|
state = { ...state, phase: "completed" };
|
|
4049
4061
|
writeWorkflowState(state, projectDir);
|
|
4050
4062
|
}
|
|
4051
4063
|
const loopTerminated = state.phase === "max-iterations" || state.phase === "circuit-breaker";
|
|
4052
4064
|
return {
|
|
4053
|
-
success: errors.length === 0 && !loopTerminated,
|
|
4065
|
+
success: errors.length === 0 && !loopTerminated && state.phase !== "interrupted",
|
|
4054
4066
|
tasksCompleted,
|
|
4055
4067
|
storiesProcessed: processedStories.size,
|
|
4056
4068
|
errors,
|
|
@@ -5660,13 +5672,14 @@ function startRenderer(options) {
|
|
|
5660
5672
|
inkInstance.rerender(/* @__PURE__ */ jsx9(App, { state, onCycleLane: () => cycleLane() }));
|
|
5661
5673
|
}
|
|
5662
5674
|
}
|
|
5675
|
+
const heartbeat = setInterval(() => {
|
|
5676
|
+
if (!cleaned) rerender();
|
|
5677
|
+
}, 200);
|
|
5663
5678
|
function onSigint() {
|
|
5664
5679
|
cleanupFull();
|
|
5665
|
-
process.kill(process.pid, "SIGINT");
|
|
5666
5680
|
}
|
|
5667
5681
|
function onSigterm() {
|
|
5668
5682
|
cleanupFull();
|
|
5669
|
-
process.kill(process.pid, "SIGTERM");
|
|
5670
5683
|
}
|
|
5671
5684
|
process.on("SIGINT", onSigint);
|
|
5672
5685
|
process.on("SIGTERM", onSigterm);
|
|
@@ -5928,6 +5941,7 @@ function startRenderer(options) {
|
|
|
5928
5941
|
function cleanupFull() {
|
|
5929
5942
|
if (cleaned) return;
|
|
5930
5943
|
cleaned = true;
|
|
5944
|
+
clearInterval(heartbeat);
|
|
5931
5945
|
try {
|
|
5932
5946
|
inkInstance.unmount();
|
|
5933
5947
|
} catch {
|
|
@@ -6160,6 +6174,7 @@ function registerRunCommand(program) {
|
|
|
6160
6174
|
info("Resuming after circuit breaker \u2014 previous findings preserved", outputOpts);
|
|
6161
6175
|
}
|
|
6162
6176
|
}
|
|
6177
|
+
const abortController = new AbortController();
|
|
6163
6178
|
const renderer = startRenderer({
|
|
6164
6179
|
quiet: !!options.quiet || isJson,
|
|
6165
6180
|
sprintState: {
|
|
@@ -6170,8 +6185,34 @@ function registerRunCommand(program) {
|
|
|
6170
6185
|
totalCost: 0
|
|
6171
6186
|
}
|
|
6172
6187
|
});
|
|
6188
|
+
let interrupted = false;
|
|
6189
|
+
const onInterrupt = () => {
|
|
6190
|
+
if (interrupted) {
|
|
6191
|
+
process.exit(1);
|
|
6192
|
+
}
|
|
6193
|
+
interrupted = true;
|
|
6194
|
+
renderer.cleanup();
|
|
6195
|
+
abortController.abort();
|
|
6196
|
+
info("Interrupted \u2014 waiting for current task to finish...", outputOpts);
|
|
6197
|
+
};
|
|
6198
|
+
process.on("SIGINT", onInterrupt);
|
|
6199
|
+
process.on("SIGTERM", onInterrupt);
|
|
6200
|
+
const sessionStartMs = Date.now();
|
|
6173
6201
|
let totalCostUsd = 0;
|
|
6174
6202
|
let storiesDone = counts.done;
|
|
6203
|
+
let currentStoryKey = "";
|
|
6204
|
+
let currentTaskName = "";
|
|
6205
|
+
const headerRefresh = setInterval(() => {
|
|
6206
|
+
if (interrupted) return;
|
|
6207
|
+
renderer.updateSprintState({
|
|
6208
|
+
storyKey: currentStoryKey,
|
|
6209
|
+
phase: currentTaskName,
|
|
6210
|
+
done: storiesDone,
|
|
6211
|
+
total: counts.total,
|
|
6212
|
+
totalCost: totalCostUsd,
|
|
6213
|
+
elapsed: formatElapsed(Date.now() - sessionStartMs)
|
|
6214
|
+
});
|
|
6215
|
+
}, 2e3);
|
|
6175
6216
|
const taskStates = {};
|
|
6176
6217
|
const taskMeta = {};
|
|
6177
6218
|
for (const [tn, task] of Object.entries(parsedWorkflow.tasks)) {
|
|
@@ -6192,12 +6233,15 @@ function registerRunCommand(program) {
|
|
|
6192
6233
|
renderer.update(event.streamEvent, event.driverName);
|
|
6193
6234
|
}
|
|
6194
6235
|
if (event.type === "dispatch-start") {
|
|
6236
|
+
currentStoryKey = event.storyKey;
|
|
6237
|
+
currentTaskName = event.taskName;
|
|
6195
6238
|
renderer.updateSprintState({
|
|
6196
6239
|
storyKey: event.storyKey,
|
|
6197
6240
|
phase: event.taskName,
|
|
6198
6241
|
done: storiesDone,
|
|
6199
6242
|
total: counts.total,
|
|
6200
|
-
totalCost: totalCostUsd
|
|
6243
|
+
totalCost: totalCostUsd,
|
|
6244
|
+
elapsed: formatElapsed(Date.now() - sessionStartMs)
|
|
6201
6245
|
});
|
|
6202
6246
|
taskStates[event.taskName] = "active";
|
|
6203
6247
|
renderer.updateWorkflowState(parsedWorkflow.flow, event.taskName, { ...taskStates }, { ...taskMeta });
|
|
@@ -6241,6 +6285,7 @@ function registerRunCommand(program) {
|
|
|
6241
6285
|
issuesPath: join15(projectDir, ".codeharness", "issues.yaml"),
|
|
6242
6286
|
runId: `run-${Date.now()}`,
|
|
6243
6287
|
projectDir,
|
|
6288
|
+
abortSignal: abortController.signal,
|
|
6244
6289
|
maxIterations,
|
|
6245
6290
|
onEvent
|
|
6246
6291
|
};
|
|
@@ -6320,8 +6365,14 @@ function registerRunCommand(program) {
|
|
|
6320
6365
|
} else {
|
|
6321
6366
|
try {
|
|
6322
6367
|
const result = await executeWorkflow(config);
|
|
6368
|
+
clearInterval(headerRefresh);
|
|
6369
|
+
process.removeListener("SIGINT", onInterrupt);
|
|
6370
|
+
process.removeListener("SIGTERM", onInterrupt);
|
|
6323
6371
|
renderer.cleanup();
|
|
6324
|
-
if (
|
|
6372
|
+
if (interrupted) {
|
|
6373
|
+
info(`Interrupted \u2014 ${result.storiesProcessed} stories processed, ${result.tasksCompleted} tasks completed. State saved \u2014 run again to resume.`, outputOpts);
|
|
6374
|
+
process.exitCode = 130;
|
|
6375
|
+
} else if (result.success) {
|
|
6325
6376
|
ok(`Workflow completed \u2014 ${result.storiesProcessed} stories processed, ${result.tasksCompleted} tasks completed in ${formatElapsed(result.durationMs)}`, outputOpts);
|
|
6326
6377
|
} else {
|
|
6327
6378
|
fail(`Workflow failed \u2014 ${result.storiesProcessed} stories processed, ${result.tasksCompleted} tasks completed, ${result.errors.length} error(s) in ${formatElapsed(result.durationMs)}`, outputOpts);
|
|
@@ -6331,6 +6382,7 @@ function registerRunCommand(program) {
|
|
|
6331
6382
|
process.exitCode = 1;
|
|
6332
6383
|
}
|
|
6333
6384
|
} catch (err) {
|
|
6385
|
+
clearInterval(headerRefresh);
|
|
6334
6386
|
renderer.cleanup();
|
|
6335
6387
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6336
6388
|
fail(`Workflow engine error: ${msg}`, outputOpts);
|
|
@@ -11115,7 +11167,7 @@ function registerTeardownCommand(program) {
|
|
|
11115
11167
|
} else if (otlpMode === "remote-routed") {
|
|
11116
11168
|
if (!options.keepDocker) {
|
|
11117
11169
|
try {
|
|
11118
|
-
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-
|
|
11170
|
+
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-PYH5XATT.js");
|
|
11119
11171
|
stopCollectorOnly2();
|
|
11120
11172
|
result.docker.stopped = true;
|
|
11121
11173
|
if (!isJson) {
|
|
@@ -11147,7 +11199,7 @@ function registerTeardownCommand(program) {
|
|
|
11147
11199
|
info("Shared stack: kept running (other projects may use it)");
|
|
11148
11200
|
}
|
|
11149
11201
|
} else if (isLegacyStack) {
|
|
11150
|
-
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-
|
|
11202
|
+
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-PYH5XATT.js");
|
|
11151
11203
|
let stackRunning = false;
|
|
11152
11204
|
try {
|
|
11153
11205
|
stackRunning = isStackRunning2(composeFile);
|
|
@@ -13378,11 +13430,38 @@ function classifyError(err) {
|
|
|
13378
13430
|
}
|
|
13379
13431
|
return "UNKNOWN";
|
|
13380
13432
|
}
|
|
13381
|
-
function
|
|
13433
|
+
function mapSdkMessages(message) {
|
|
13382
13434
|
const type = message.type;
|
|
13435
|
+
const events = [];
|
|
13436
|
+
if (type === "assistant") {
|
|
13437
|
+
const msg = message.message;
|
|
13438
|
+
if (!msg) return events;
|
|
13439
|
+
const content = msg.content;
|
|
13440
|
+
if (!Array.isArray(content)) return events;
|
|
13441
|
+
for (const block of content) {
|
|
13442
|
+
if (block.type === "tool_use") {
|
|
13443
|
+
const name = block.name;
|
|
13444
|
+
const id = block.id;
|
|
13445
|
+
const input = block.input;
|
|
13446
|
+
if (typeof name === "string") {
|
|
13447
|
+
events.push({ type: "tool-start", name, id: typeof id === "string" ? id : "" });
|
|
13448
|
+
if (input != null) {
|
|
13449
|
+
events.push({ type: "tool-input", partial: typeof input === "string" ? input : JSON.stringify(input) });
|
|
13450
|
+
}
|
|
13451
|
+
events.push({ type: "tool-complete" });
|
|
13452
|
+
}
|
|
13453
|
+
} else if (block.type === "text") {
|
|
13454
|
+
const text = block.text;
|
|
13455
|
+
if (typeof text === "string" && text.length > 0) {
|
|
13456
|
+
events.push({ type: "text", text });
|
|
13457
|
+
}
|
|
13458
|
+
}
|
|
13459
|
+
}
|
|
13460
|
+
return events;
|
|
13461
|
+
}
|
|
13383
13462
|
if (type === "stream_event") {
|
|
13384
13463
|
const event = message.event;
|
|
13385
|
-
if (!event || typeof event !== "object") return
|
|
13464
|
+
if (!event || typeof event !== "object") return events;
|
|
13386
13465
|
const eventType = event.type;
|
|
13387
13466
|
if (eventType === "content_block_start") {
|
|
13388
13467
|
const contentBlock = event.content_block;
|
|
@@ -13390,34 +13469,26 @@ function mapSdkMessage(message) {
|
|
|
13390
13469
|
const name = contentBlock.name;
|
|
13391
13470
|
const id = contentBlock.id;
|
|
13392
13471
|
if (typeof name === "string" && typeof id === "string") {
|
|
13393
|
-
|
|
13472
|
+
events.push({ type: "tool-start", name, id });
|
|
13394
13473
|
}
|
|
13395
13474
|
}
|
|
13396
|
-
return
|
|
13475
|
+
return events;
|
|
13397
13476
|
}
|
|
13398
13477
|
if (eventType === "content_block_delta") {
|
|
13399
13478
|
const delta = event.delta;
|
|
13400
|
-
if (!delta) return
|
|
13401
|
-
if (delta.type === "input_json_delta") {
|
|
13402
|
-
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
}
|
|
13406
|
-
return null;
|
|
13407
|
-
}
|
|
13408
|
-
if (delta.type === "text_delta") {
|
|
13409
|
-
const text = delta.text;
|
|
13410
|
-
if (typeof text === "string") {
|
|
13411
|
-
return { type: "text", text };
|
|
13412
|
-
}
|
|
13413
|
-
return null;
|
|
13479
|
+
if (!delta) return events;
|
|
13480
|
+
if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
13481
|
+
events.push({ type: "tool-input", partial: delta.partial_json });
|
|
13482
|
+
} else if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
13483
|
+
events.push({ type: "text", text: delta.text });
|
|
13414
13484
|
}
|
|
13415
|
-
return
|
|
13485
|
+
return events;
|
|
13416
13486
|
}
|
|
13417
13487
|
if (eventType === "content_block_stop") {
|
|
13418
|
-
|
|
13488
|
+
events.push({ type: "tool-complete" });
|
|
13489
|
+
return events;
|
|
13419
13490
|
}
|
|
13420
|
-
return
|
|
13491
|
+
return events;
|
|
13421
13492
|
}
|
|
13422
13493
|
if (type === "system") {
|
|
13423
13494
|
const subtype = message.subtype;
|
|
@@ -13425,12 +13496,12 @@ function mapSdkMessage(message) {
|
|
|
13425
13496
|
const attempt = message.attempt;
|
|
13426
13497
|
const delay = message.retry_delay_ms;
|
|
13427
13498
|
if (typeof attempt === "number" && typeof delay === "number") {
|
|
13428
|
-
|
|
13499
|
+
events.push({ type: "retry", attempt, delay });
|
|
13429
13500
|
}
|
|
13430
13501
|
}
|
|
13431
|
-
return
|
|
13502
|
+
return events;
|
|
13432
13503
|
}
|
|
13433
|
-
return
|
|
13504
|
+
return events;
|
|
13434
13505
|
}
|
|
13435
13506
|
var ClaudeCodeDriver = class {
|
|
13436
13507
|
name = "claude-code";
|
|
@@ -13491,8 +13562,7 @@ var ClaudeCodeDriver = class {
|
|
|
13491
13562
|
yieldedResult = true;
|
|
13492
13563
|
continue;
|
|
13493
13564
|
}
|
|
13494
|
-
const streamEvent
|
|
13495
|
-
if (streamEvent) {
|
|
13565
|
+
for (const streamEvent of mapSdkMessages(msg)) {
|
|
13496
13566
|
yield streamEvent;
|
|
13497
13567
|
}
|
|
13498
13568
|
}
|
|
@@ -14025,7 +14095,7 @@ function registerDriversCommand(program) {
|
|
|
14025
14095
|
}
|
|
14026
14096
|
|
|
14027
14097
|
// src/index.ts
|
|
14028
|
-
var VERSION = true ? "0.29.
|
|
14098
|
+
var VERSION = true ? "0.29.4" : "0.0.0-dev";
|
|
14029
14099
|
function createProgram() {
|
|
14030
14100
|
const program = new Command();
|
|
14031
14101
|
program.name("codeharness").description("Makes autonomous coding agents produce software that actually works").version(VERSION).option("--json", "Output in machine-readable JSON format");
|