@hmharness/agent 0.11.0 → 0.12.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/pipeline.d.ts +14 -0
- package/dist/pipeline.js +19 -1
- package/dist/project.js +7 -0
- package/package.json +1 -1
package/dist/pipeline.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ export interface PipelineReport {
|
|
|
21
21
|
finalVerdict: 'PASS' | 'FAIL' | 'none';
|
|
22
22
|
stages: StageRecord[];
|
|
23
23
|
repairsUsed: number;
|
|
24
|
+
/** set when opts.release ran on a PASS verdict (ADR-0008) */
|
|
25
|
+
release?: {
|
|
26
|
+
projectId: string;
|
|
27
|
+
version: string;
|
|
28
|
+
checkpointId: string;
|
|
29
|
+
};
|
|
24
30
|
}
|
|
25
31
|
export interface PipelineOptions {
|
|
26
32
|
task: string;
|
|
@@ -53,6 +59,14 @@ export interface PipelineOptions {
|
|
|
53
59
|
/** injectable runner (tests); default = domain-harmony runDeviceTest */
|
|
54
60
|
runDeviceTestImpl?: (o: DeviceTestOptions) => Promise<DeviceTestStep[]>;
|
|
55
61
|
};
|
|
62
|
+
/** V3 release slice (ADR-0008): when the final verdict is PASS, bind the
|
|
63
|
+
* work to the workspace's Project Runtime (M8): attach the run, checkpoint
|
|
64
|
+
* the tree, and record a release pinned to that checkpoint. FAIL/budget
|
|
65
|
+
* never release - no verdict, no version. */
|
|
66
|
+
release?: {
|
|
67
|
+
version: string;
|
|
68
|
+
notes?: string;
|
|
69
|
+
};
|
|
56
70
|
}
|
|
57
71
|
/** Pull the judge's verdict line out of the final text; absence = FAIL
|
|
58
72
|
* (an judge that did not follow the contract did not render a verdict). */
|
package/dist/pipeline.js
CHANGED
|
@@ -73,6 +73,7 @@ export async function runPipeline(opts) {
|
|
|
73
73
|
let repairs = 0;
|
|
74
74
|
let spent = 0;
|
|
75
75
|
let status = 'completed';
|
|
76
|
+
let releaseTick;
|
|
76
77
|
const startedAt = new Date().toISOString();
|
|
77
78
|
const runStage = async (stage, attempt, directive) => {
|
|
78
79
|
// maxTurns is only a soft checkpoint in the kernel loop - the HARD caps
|
|
@@ -183,7 +184,23 @@ export async function runPipeline(opts) {
|
|
|
183
184
|
}
|
|
184
185
|
judge = await runStage('judge', repairs + 1, DIRECTIVES.judge(opts.task, evidenceSummary(stages)));
|
|
185
186
|
}
|
|
186
|
-
|
|
187
|
+
const verdict = judge && judge.verdict !== 'n/a' ? judge.verdict : 'FAIL';
|
|
188
|
+
// V3 release slice: only a PASS verdict earns a version (ADR-0008)
|
|
189
|
+
if (verdict === 'PASS' && opts.release) {
|
|
190
|
+
try {
|
|
191
|
+
const P = await import("./project.js");
|
|
192
|
+
const proj = await P.projectFor(opts.home, opts.ctx.cwd);
|
|
193
|
+
await P.attachRun(opts.home, proj, pipelineId);
|
|
194
|
+
const cp = await P.checkpointProject(opts.home, proj, `pipeline ${pipelineId}`);
|
|
195
|
+
await P.releaseProject(opts.home, proj, opts.release.version, opts.release.notes ?? `pipeline ${pipelineId} VERDICT: PASS (${stages.length} stages, ${repairs} repairs)`);
|
|
196
|
+
releaseTick = { projectId: proj.projectId, version: opts.release.version, checkpointId: cp.id };
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
// release is bookkeeping - record the failure, never fail the pipeline
|
|
200
|
+
stages.push({ stage: 'judge', attempt: 0, verdict: 'n/a', text: `release binding failed: ${String(err).slice(0, 200)}`, turns: 0, toolUses: 0, reason: 'final' });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return await finish(verdict);
|
|
187
204
|
}
|
|
188
205
|
catch (err) {
|
|
189
206
|
status = 'error';
|
|
@@ -200,6 +217,7 @@ export async function runPipeline(opts) {
|
|
|
200
217
|
finalVerdict: verdict,
|
|
201
218
|
stages,
|
|
202
219
|
repairsUsed: repairs,
|
|
220
|
+
...(releaseTick ? { release: releaseTick } : {}),
|
|
203
221
|
};
|
|
204
222
|
try {
|
|
205
223
|
await writeFile(join(dir, 'pipeline.report.json'), JSON.stringify(report, null, 2) + '\n', 'utf8');
|
package/dist/project.js
CHANGED
|
@@ -192,6 +192,13 @@ export async function checkpointProject(home, rec, label) {
|
|
|
192
192
|
else {
|
|
193
193
|
const rel = `artifacts/cp-${Date.now().toString(36)}`;
|
|
194
194
|
const dest = join(projDir(home, rec.projectId), rel);
|
|
195
|
+
// the copy fallback cannot handle the workspace containing the destination
|
|
196
|
+
// (workspace == home in degenerate setups) - say so plainly instead of
|
|
197
|
+
// letting cp throw EINVAL halfway through
|
|
198
|
+
const norm = (p) => resolve(p).replace(/[\\/]+$/, '').toLowerCase();
|
|
199
|
+
if (norm(dest).startsWith(norm(rec.workspace) + '\\') || norm(dest).startsWith(norm(rec.workspace) + '/')) {
|
|
200
|
+
throw new Error(`checkpoint copy fallback requires the project workspace (${rec.workspace}) to be outside HMH_HOME (${home})`);
|
|
201
|
+
}
|
|
195
202
|
await mkdir(dest, { recursive: true });
|
|
196
203
|
await cp(rec.workspace, dest, { recursive: true, filter: (src) => !COPY_SKIP.has(src.split(/[\\/]/).pop() ?? '') });
|
|
197
204
|
let files = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmharness/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "hmharness agent execution layer: base tools, system prompt, sub-agent spawn, and the shared task runner that frontends (cli, web) drive.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|