harnery 0.27.0 → 0.29.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.
Files changed (62) hide show
  1. package/dist/commands/browse.d.ts.map +1 -1
  2. package/dist/commands/browse.js +27 -1
  3. package/dist/commands/work.d.ts +2 -0
  4. package/dist/commands/work.d.ts.map +1 -1
  5. package/dist/commands/work.js +74 -5
  6. package/dist/core/harnesses/types.d.ts +5 -0
  7. package/dist/core/harnesses/types.d.ts.map +1 -1
  8. package/dist/core/work/index.d.ts +1 -1
  9. package/dist/core/work/index.d.ts.map +1 -1
  10. package/dist/core/work/index.js +1 -1
  11. package/dist/core/work/runner.d.ts.map +1 -1
  12. package/dist/core/work/runner.js +8 -1
  13. package/dist/core/work/state.d.ts +24 -1
  14. package/dist/core/work/state.d.ts.map +1 -1
  15. package/dist/core/work/state.js +108 -14
  16. package/dist/core/workflow/approvals.d.ts.map +1 -1
  17. package/dist/core/workflow/approvals.js +3 -6
  18. package/dist/core/workflow/attempt-context.d.ts.map +1 -1
  19. package/dist/core/workflow/attempt-context.js +38 -1
  20. package/dist/core/workflow/engine.d.ts.map +1 -1
  21. package/dist/core/workflow/engine.js +20 -19
  22. package/dist/core/workflow/journal.d.ts +30 -0
  23. package/dist/core/workflow/journal.d.ts.map +1 -0
  24. package/dist/core/workflow/journal.js +70 -0
  25. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  26. package/dist/core/workflow/spawn-claude.js +8 -0
  27. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  28. package/dist/core/workflow/spawn-codex.js +8 -0
  29. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  30. package/dist/core/workflow/spawn-cursor.js +8 -0
  31. package/dist/core/workflow/types.d.ts +11 -0
  32. package/dist/core/workflow/types.d.ts.map +1 -1
  33. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  34. package/dist/core/workflow/workspaces/local-git.js +101 -20
  35. package/dist/core/workflow/workspaces/state.d.ts +1 -1
  36. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  37. package/dist/core/workflow/workspaces/state.js +9 -30
  38. package/dist/lib/browser/client.d.ts +50 -0
  39. package/dist/lib/browser/client.d.ts.map +1 -1
  40. package/dist/lib/browser/client.js +103 -0
  41. package/dist/lib/exec.d.ts +4 -0
  42. package/dist/lib/exec.d.ts.map +1 -1
  43. package/dist/lib/exec.js +6 -1
  44. package/package.json +1 -1
  45. package/src/commands/browse.ts +33 -1
  46. package/src/commands/work.ts +109 -14
  47. package/src/core/harnesses/types.ts +5 -0
  48. package/src/core/work/index.ts +1 -0
  49. package/src/core/work/runner.ts +8 -0
  50. package/src/core/work/state.ts +177 -27
  51. package/src/core/workflow/approvals.ts +3 -6
  52. package/src/core/workflow/attempt-context.ts +47 -1
  53. package/src/core/workflow/engine.ts +48 -33
  54. package/src/core/workflow/journal.ts +90 -0
  55. package/src/core/workflow/spawn-claude.ts +8 -0
  56. package/src/core/workflow/spawn-codex.ts +8 -0
  57. package/src/core/workflow/spawn-cursor.ts +8 -0
  58. package/src/core/workflow/types.ts +12 -0
  59. package/src/core/workflow/workspaces/local-git.ts +112 -22
  60. package/src/core/workflow/workspaces/state.ts +13 -33
  61. package/src/lib/browser/client.ts +129 -0
  62. package/src/lib/exec.ts +10 -1
@@ -1 +1 @@
1
- {"version":3,"file":"browse.d.ts","sourceRoot":"","sources":["../../src/commands/browse.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAgJ1E,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,WAAW,EACtB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA6PN"}
1
+ {"version":3,"file":"browse.d.ts","sourceRoot":"","sources":["../../src/commands/browse.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAiJ1E,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,WAAW,EACtB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CAiQN"}
@@ -55,6 +55,7 @@ export function registerBrowseCommand(program, emitParam, context) {
55
55
  .option("--press <key>", "Press a key after navigation/fill (e.g., Enter)")
56
56
  .option("--wait-for <selector>", "Wait for this selector before capturing output")
57
57
  .option("--evaluate <js>", "Run JS in the page context after navigation; result printed to stdout")
58
+ .option("--capture-evaluate <js>", "In trio mode, evaluate JS inside the exact screenshot viewport immediately before capture; result is written as captureEval")
58
59
  .option("--batch <steps>", "Run multiple steps in one session, semicolon-separated. Each step is one of: " +
59
60
  "`click <selector>`, `fill <selector=>value>`, `press <key>`, `wait <selector|ms>`, `eval <js>`, `reload`. " +
60
61
  'Example: `--batch "click button; wait 1500; reload; wait 3000"`. `reload` preserves sessionStorage + cookies, which is how to repro sessionStorage-restored UI state.')
@@ -312,6 +313,9 @@ async function runBrowse(url, opts, context) {
312
313
  devOverlay = await captureDevOverlay(browser.currentPage);
313
314
  }
314
315
  if (printMode) {
316
+ if (opts.captureEvaluate) {
317
+ throw new Error("--capture-evaluate requires trio mode so its result can be bound to a screenshot.");
318
+ }
315
319
  if (opts.baseline || opts.diff) {
316
320
  throw new Error("--baseline / --diff require trio mode (screenshot file). Remove --snapshot/--html/--json or capture the screenshot first.");
317
321
  }
@@ -551,11 +555,28 @@ async function runTrioMode(browser, navResult, opts, evalResult, visibility, wid
551
555
  const skipScreenshot = opts.screenshot === false || opts.domOnly === true;
552
556
  let pngPath;
553
557
  let pngBytes;
558
+ let captureEvalResult;
559
+ let captureViewport;
560
+ let captureEvidence;
554
561
  if (!skipScreenshot) {
555
562
  pngPath = `${prefix}.png`;
556
- pngBytes = await browser.screenshot(pngPath, { fullPage: opts.fullPage !== false });
563
+ if (opts.captureEvaluate) {
564
+ const captured = await browser.screenshotWithEvaluation(pngPath, opts.captureEvaluate, {
565
+ fullPage: opts.fullPage !== false,
566
+ });
567
+ pngBytes = captured.bytes;
568
+ captureEvalResult = captured.evaluation;
569
+ captureViewport = captured.viewport;
570
+ captureEvidence = captured.evidence;
571
+ }
572
+ else {
573
+ pngBytes = await browser.screenshot(pngPath, { fullPage: opts.fullPage !== false });
574
+ }
557
575
  written.push(pngPath);
558
576
  }
577
+ else if (opts.captureEvaluate) {
578
+ throw new Error("--capture-evaluate requires a screenshot; remove --no-screenshot / --dom-only.");
579
+ }
559
580
  const htmlPath = `${prefix}.html`;
560
581
  const html = await browser.htmlContent(opts.selector);
561
582
  writeFileSync(htmlPath, html);
@@ -577,6 +598,11 @@ async function runTrioMode(browser, navResult, opts, evalResult, visibility, wid
577
598
  envelope.screenshotBytes = pngBytes;
578
599
  if (opts.evaluate)
579
600
  envelope.eval = evalResult;
601
+ if (opts.captureEvaluate) {
602
+ envelope.captureEval = captureEvalResult;
603
+ envelope.captureViewport = captureViewport;
604
+ envelope.captureEvidence = captureEvidence;
605
+ }
580
606
  if (opts.networkHar)
581
607
  envelope.har = resolve(opts.networkHar);
582
608
  if (visibility)
@@ -1,4 +1,6 @@
1
1
  import type { Command } from "commander";
2
2
  import type { EmitContext } from "../commander.js";
3
+ import { type WorkAttempt } from "../core/work/index.js";
3
4
  export declare function registerWorkCommand(program: Command, emit: EmitContext): void;
5
+ export declare function renderAttemptRow(attempt: WorkAttempt): string;
4
6
  //# sourceMappingURL=work.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"work.d.ts","sourceRoot":"","sources":["../../src/commands/work.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAuDnD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CA8F7E"}
1
+ {"version":3,"file":"work.d.ts","sourceRoot":"","sources":["../../src/commands/work.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAUnD,OAAO,EAUL,KAAK,WAAW,EAEjB,MAAM,uBAAuB,CAAC;AAuC/B,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CA8F7E;AA0OD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAM7D"}
@@ -1,9 +1,11 @@
1
+ import { resolve } from "node:path";
1
2
  import { workflowSubscriptionOnly } from "../core/config.js";
2
3
  import { createBuiltinHarnessRegistry, harnessProofInputs, probeBinaryVersion, } from "../core/harnesses/index.js";
3
4
  import { findCoordRoot } from "../core/hooks/resolve/coord-root.js";
4
5
  import { loadPolicyFile } from "../core/policy/index.js";
5
6
  import { acceptWorkItem, cancelWorkItem, createWorkItem, listWorkItems, readWorkItem, reconcileAllWorkItems, reconcileWorkItem, reopenWorkItem, runWorkItem, } from "../core/work/index.js";
6
7
  import { WorkflowParkedError } from "../core/workflow/index.js";
8
+ const MAX_ATTEMPT_JOURNAL_ERROR_RENDER = 240;
7
9
  export function registerWorkCommand(program, emit) {
8
10
  const registry = createBuiltinHarnessRegistry();
9
11
  const harnesses = registry.ids();
@@ -110,6 +112,7 @@ function registerRunCommand(work, name, retry, registry, harnesses, emit) {
110
112
  .option("--allow-api-billing", "Permit API-key override billing")
111
113
  .option("--policy <file>", "Host policy JSON/JSONC")
112
114
  .option("--isolation <mode>", "shared | worktree | sandbox | remote")
115
+ .option("--workspace-root <dir>", "Writable root the isolated workspace is allocated under")
113
116
  .option("--approval-to <address>", "Address durable ASK requests")
114
117
  .option("--actor <name>", "Actor recorded in attempt receipts")
115
118
  .option("--json", "Emit the workflow report or parked result as JSON")
@@ -118,7 +121,21 @@ function registerRunCommand(work, name, retry, registry, harnesses, emit) {
118
121
  if (opts.harness && !registry.get(opts.harness)) {
119
122
  throw new Error(`unknown harness ${JSON.stringify(opts.harness)}`);
120
123
  }
124
+ if (opts.workspaceRoot && opts.isolation !== "worktree") {
125
+ throw new Error("--workspace-root requires --isolation worktree");
126
+ }
121
127
  try {
128
+ // An isolated attempt needs a provider AND the writable root it may
129
+ // allocate under; the engine refuses to guess either. Without them a
130
+ // worktree request degrades to shared, which the proof records as
131
+ // requested vs effective isolation and the reporting below surfaces.
132
+ const { createLocalGitWorktreeProvider } = await import("../core/workflow/index.js");
133
+ const workspace = opts.workspaceRoot
134
+ ? {
135
+ provider: createLocalGitWorktreeProvider({ coordRoot }),
136
+ writableRoots: [resolve(opts.workspaceRoot)],
137
+ }
138
+ : undefined;
122
139
  const report = await runWorkItem({
123
140
  coordRoot,
124
141
  workId,
@@ -137,6 +154,7 @@ function registerRunCommand(work, name, retry, registry, harnesses, emit) {
137
154
  approvalMode: "park",
138
155
  approvalAddressee: opts.approvalTo,
139
156
  isolation: opts.isolation,
157
+ workspace,
140
158
  networkAccess: "enabled",
141
159
  },
142
160
  });
@@ -145,7 +163,16 @@ function registerRunCommand(work, name, retry, registry, harnesses, emit) {
145
163
  emit.data(report);
146
164
  }
147
165
  else {
148
- emit.text(`work ${workId}: run ${report.runId} finished\nproof: ${report.proofPath}\n` +
166
+ // A requested isolation the run could not honour is recorded in the
167
+ // proof as requested vs effective, which nobody reads on a green run.
168
+ // Say it here instead, so a silent downgrade to shared cannot pass for
169
+ // an isolated attempt.
170
+ const degraded = opts.isolation && opts.isolation !== "shared" && !report.workspaceBinding
171
+ ? `note: ran shared; ${opts.isolation} isolation was requested but not allocated` +
172
+ `${opts.workspaceRoot ? "" : " (no --workspace-root given)"}\n`
173
+ : "";
174
+ emit.text(`work ${workId}: run ${report.runId} finished\n${degraded}` +
175
+ `proof: ${report.proofPath}\n` +
149
176
  `next: harn work reconcile ${workId}\n`);
150
177
  }
151
178
  }
@@ -182,14 +209,43 @@ function registerGovernanceCommand(work, name, emit) {
182
209
  : "Explicitly reopen terminal or blocked work for a future attempt.")
183
210
  .option("--actor <name>", "Actor recorded in the governance receipt")
184
211
  .option("--reason <text>", "Bounded decision reason")
185
- .option("--json", "Emit the complete work record as JSON")
186
- .action((workId, opts) => {
212
+ .option("--json", "Emit the complete work record as JSON");
213
+ const command = work.commands.at(-1);
214
+ if (name === "reopen") {
215
+ command.option("--finding <text>", "Correction the next attempt must address (repeatable)", collectValue, []);
216
+ }
217
+ if (name === "accept") {
218
+ command.option("--dispose <id=outcome[:reason]>", "Dispose of an open finding: fixed, or deferred:<reason> (repeatable)", collectValue, []);
219
+ }
220
+ command.action((workId, opts) => {
187
221
  withWorkRoot(emit, (coordRoot) => {
188
222
  const fn = name === "accept" ? acceptWorkItem : name === "cancel" ? cancelWorkItem : reopenWorkItem;
189
- emitWork(fn(coordRoot, workId, opts), opts.json, emit, true);
223
+ emitWork(fn(coordRoot, workId, {
224
+ ...opts,
225
+ ...(opts.finding?.length ? { findings: opts.finding } : {}),
226
+ ...(opts.dispose?.length ? { dispositions: opts.dispose.map(parseDisposition) } : {}),
227
+ }), opts.json, emit, true);
190
228
  });
191
229
  });
192
230
  }
231
+ function collectValue(value, previous) {
232
+ return [...previous, value];
233
+ }
234
+ /** `<id>=fixed` or `<id>=deferred:<reason>`. Deferring without a reason is
235
+ * rejected downstream, so a lapsed finding cannot look like a decision. */
236
+ function parseDisposition(input) {
237
+ const split = input.indexOf("=");
238
+ if (split <= 0)
239
+ throw new Error(`invalid --dispose ${JSON.stringify(input)}; expected <id>=<outcome>`);
240
+ const id = input.slice(0, split).trim();
241
+ const rest = input.slice(split + 1).trim();
242
+ const [outcome, ...reasonParts] = rest.split(":");
243
+ if (outcome !== "fixed" && outcome !== "deferred") {
244
+ throw new Error(`invalid --dispose outcome ${JSON.stringify(outcome)}; expected fixed or deferred`);
245
+ }
246
+ const reason = reasonParts.join(":").trim();
247
+ return { id, outcome, ...(reason ? { reason } : {}) };
248
+ }
193
249
  function emitWork(record, json, emit, detail = false) {
194
250
  if (json) {
195
251
  emit.config({ format: "json" });
@@ -215,7 +271,7 @@ function emitWork(record, json, emit, detail = false) {
215
271
  lines.push("acceptance:", ...record.intent.acceptance.map((item) => ` - ${item}`));
216
272
  }
217
273
  if (projection.attempts.length) {
218
- lines.push("attempts:", ...projection.attempts.map((attempt) => ` ${attempt.number}. ${attempt.run_id} ${attempt.status}`));
274
+ lines.push("attempts:", ...projection.attempts.map(renderAttemptRow));
219
275
  }
220
276
  if (projection.approval_id)
221
277
  lines.push(`approval: ${projection.approval_id}`);
@@ -223,6 +279,19 @@ function emitWork(record, json, emit, detail = false) {
223
279
  lines.push(`proof: ${projection.proof_path}`);
224
280
  emit.text(`${lines.join("\n")}\n`);
225
281
  }
282
+ export function renderAttemptRow(attempt) {
283
+ const statusDetail = attempt.status === "journal_unreadable"
284
+ ? `: ${renderAttemptJournalError(attempt.journal_error)}`
285
+ : "";
286
+ return ` ${attempt.number}. ${attempt.run_id} ${attempt.status}${statusDetail}`;
287
+ }
288
+ function renderAttemptJournalError(error) {
289
+ const normalized = (error ?? "").replace(/\s+/g, " ").trim();
290
+ const value = normalized || "unknown journal read error";
291
+ if (value.length <= MAX_ATTEMPT_JOURNAL_ERROR_RENDER)
292
+ return value;
293
+ return `${value.slice(0, MAX_ATTEMPT_JOURNAL_ERROR_RENDER - 3).trimEnd()}...`;
294
+ }
226
295
  function renderWorkRow(record) {
227
296
  const projection = record.projection;
228
297
  return `${record.intent.id} ${projection.state.padEnd(17)} ${projection.attempts_used}/${record.intent.max_attempts} ${projection.next_action.padEnd(21)} ${record.intent.title}`;
@@ -56,6 +56,11 @@ export interface HarnessRawResult {
56
56
  exitCode: number;
57
57
  durationMs: number;
58
58
  resultFileText?: string;
59
+ /** The host killed this child for exceeding its timeout. A vendor CLI that
60
+ * handles the signal cleanly still exits 0, so exit code alone cannot tell a
61
+ * kill from a finish; without this a timed-out child reads as a success with
62
+ * an empty answer. */
63
+ timedOut?: boolean;
59
64
  }
60
65
  export interface HarnessBenchFixture {
61
66
  raw: HarnessRawResult;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/harnesses/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE/E;uDACuD;AACvD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;AAEpF,eAAO,MAAM,6BAA6B,2TAoBhC,CAAC;AAEX,MAAM,MAAM,0BAA0B,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AAExF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,iBAAiB,CAAC;IAC3B;iFAC6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,0BAA0B,EAAE,eAAe,CAAC,CAAC;AAEtF;;;0DAG0D;AAC1D,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC,WAAW,GAAG,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9D,iEAAiE;IACjE,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,SAAS,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,gBAAgB,CAAC;IAClC,SAAS,EAAE,UAAU,CAAC;IACtB,WAAW,EAAE,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;IACxC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,YAAY,EAAE,mBAAmB,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C;uEACmE;IACnE,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;CAC9C;AAED;oEACoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,gBAAgB,CAAC;IACtB,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG;QAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;wDAEwD;AACxD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,iBAAiB,CAAC;IACnF,eAAe,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,WAAW,CAAC;IACxD,OAAO,EAAE,mBAAmB,CAAC;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/harnesses/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE/E;uDACuD;AACvD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;AAEpF,eAAO,MAAM,6BAA6B,2TAoBhC,CAAC;AAEX,MAAM,MAAM,0BAA0B,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AAExF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,iBAAiB,CAAC;IAC3B;iFAC6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,0BAA0B,EAAE,eAAe,CAAC,CAAC;AAEtF;;;0DAG0D;AAC1D,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC,WAAW,GAAG,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9D,iEAAiE;IACjE,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,SAAS,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,gBAAgB,CAAC;IAClC,SAAS,EAAE,UAAU,CAAC;IACtB,WAAW,EAAE,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;IACxC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,YAAY,EAAE,mBAAmB,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C;uEACmE;IACnE,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;CAC9C;AAED;oEACoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;0BAGsB;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,gBAAgB,CAAC;IACtB,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG;QAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;wDAEwD;AACxD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,iBAAiB,CAAC;IACnF,eAAe,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,WAAW,CAAC;IACxD,OAAO,EAAE,mBAAmB,CAAC;CAC9B"}
@@ -1,3 +1,3 @@
1
1
  export { type RunWorkItemInput, runWorkItem } from "./runner.js";
2
- export { acceptWorkItem, assertWorkId, type CreateWorkItemInput, cancelWorkItem, createWorkItem, listWorkItems, newWorkId, readWorkItem, reconcileAllWorkItems, reconcileWorkItem, reopenWorkItem, WORK_EVENT_SCHEMA_VERSION, WORK_INTENT_SCHEMA_VERSION, type WorkAttempt, type WorkAttemptTrigger, type WorkEvent, type WorkEventType, type WorkIntent, type WorkNextAction, type WorkProjection, type WorkRecord, type WorkState, } from "./state.js";
2
+ export { acceptWorkItem, assertWorkId, type CreateWorkItemInput, cancelWorkItem, createWorkItem, listWorkItems, newWorkId, openOperatorFindings, readWorkItem, reconcileAllWorkItems, reconcileWorkItem, reopenWorkItem, WORK_EVENT_SCHEMA_VERSION, WORK_INTENT_SCHEMA_VERSION, type WorkAttempt, type WorkAttemptTrigger, type WorkEvent, type WorkEventType, type WorkIntent, type WorkNextAction, type WorkProjection, type WorkRecord, type WorkState, } from "./state.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/work/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,cAAc,EACd,YAAY,EACZ,KAAK,mBAAmB,EACxB,cAAc,EACd,cAAc,EACd,aAAa,EACb,SAAS,EACT,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,yBAAyB,EACzB,0BAA0B,EAC1B,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,SAAS,GACf,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/work/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,cAAc,EACd,YAAY,EACZ,KAAK,mBAAmB,EACxB,cAAc,EACd,cAAc,EACd,aAAa,EACb,SAAS,EACT,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,yBAAyB,EACzB,0BAA0B,EAC1B,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,SAAS,GACf,MAAM,YAAY,CAAC"}
@@ -1,2 +1,2 @@
1
1
  export { runWorkItem } from "./runner.js";
2
- export { acceptWorkItem, assertWorkId, cancelWorkItem, createWorkItem, listWorkItems, newWorkId, readWorkItem, reconcileAllWorkItems, reconcileWorkItem, reopenWorkItem, WORK_EVENT_SCHEMA_VERSION, WORK_INTENT_SCHEMA_VERSION, } from "./state.js";
2
+ export { acceptWorkItem, assertWorkId, cancelWorkItem, createWorkItem, listWorkItems, newWorkId, openOperatorFindings, readWorkItem, reconcileAllWorkItems, reconcileWorkItem, reopenWorkItem, WORK_EVENT_SCHEMA_VERSION, WORK_INTENT_SCHEMA_VERSION, } from "./state.js";
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/core/work/runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,SAAS,EAGV,MAAM,sBAAsB,CAAC;AAgB9B,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,CACV,UAAU,EACV,WAAW,GAAG,OAAO,GAAG,aAAa,GAAG,YAAY,GAAG,aAAa,GAAG,gBAAgB,CACxF,CAAC;IACF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAqF7E"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/core/work/runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,SAAS,EAGV,MAAM,sBAAsB,CAAC;AAiB9B,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,CACV,UAAU,EACV,WAAW,GAAG,OAAO,GAAG,aAAa,GAAG,YAAY,GAAG,aAAa,GAAG,gBAAgB,CACxF,CAAC;IACF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CA4F7E"}
@@ -2,7 +2,7 @@ import { resolve } from "node:path";
2
2
  import { runWorkflow } from "../workflow/engine.js";
3
3
  import { readWorkflowProof } from "../workflow/proof.js";
4
4
  import { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "../workflow/types.js";
5
- import { acquireWorkLease, appendReconcileIfChanged, appendWorkEvent, assertWorkflowUnchanged, boundedActor, newWorkflowRunId, readWorkItemIgnoringLease, } from "./state.js";
5
+ import { acquireWorkLease, appendReconcileIfChanged, appendWorkEvent, assertWorkflowUnchanged, boundedActor, newWorkflowRunId, openOperatorFindings, readWorkItemIgnoringLease, } from "./state.js";
6
6
  export async function runWorkItem(input) {
7
7
  const coordRoot = resolve(input.coordRoot);
8
8
  const release = acquireWorkLease(coordRoot, input.workId);
@@ -49,11 +49,18 @@ export async function runWorkItem(input) {
49
49
  }
50
50
  prior = priorContext(coordRoot, latest);
51
51
  }
52
+ const open = openOperatorFindings(coordRoot, input.workId);
53
+ const findings = open.findings.map((finding) => ({
54
+ id: finding.id,
55
+ actor: open.actor,
56
+ statement: finding.statement,
57
+ }));
52
58
  const attemptContext = {
53
59
  schema_version: WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION,
54
60
  number: attempt,
55
61
  trigger,
56
62
  ...(prior ? { prior } : {}),
63
+ ...(findings.length === 0 ? {} : { findings }),
57
64
  };
58
65
  const runId = newWorkflowRunId();
59
66
  appendWorkEvent(coordRoot, input.workId, {
@@ -37,17 +37,32 @@ export interface WorkEvent {
37
37
  proof_sha256?: string;
38
38
  terminal_attestation_sha256?: string;
39
39
  accepted_unknowns?: string[];
40
+ /** Operator corrections raised with a reopen. Authored by a human against a
41
+ * prior attempt, carried into the next attempt, and open until disposed. */
42
+ findings?: WorkOperatorFinding[];
43
+ /** How an operator disposed of each open finding at acceptance. */
44
+ dispositions?: WorkFindingDisposition[];
40
45
  state?: WorkState;
41
46
  next_action?: WorkNextAction;
42
47
  }
48
+ export interface WorkOperatorFinding {
49
+ id: string;
50
+ statement: string;
51
+ }
52
+ export interface WorkFindingDisposition {
53
+ id: string;
54
+ outcome: "fixed" | "deferred";
55
+ reason?: string;
56
+ }
43
57
  export interface WorkAttempt {
44
58
  number: number;
45
59
  run_id: string;
46
60
  started_at: string;
47
61
  trigger?: WorkAttemptTrigger;
48
- status: "running" | "parked" | "succeeded" | "failed" | "lost";
62
+ status: "running" | "parked" | "succeeded" | "failed" | "lost" | "journal_unreadable";
49
63
  approval_id?: string;
50
64
  proof_path?: string;
65
+ journal_error?: string;
51
66
  }
52
67
  export interface WorkProjection {
53
68
  id: string;
@@ -90,6 +105,7 @@ export declare function acceptWorkItem(coordRoot: string, workId: string, input?
90
105
  actor?: string;
91
106
  reason?: string;
92
107
  acceptedUnknowns?: readonly string[];
108
+ dispositions?: readonly WorkFindingDisposition[];
93
109
  }): WorkRecord;
94
110
  export declare function cancelWorkItem(coordRoot: string, workId: string, input?: {
95
111
  actor?: string;
@@ -98,7 +114,14 @@ export declare function cancelWorkItem(coordRoot: string, workId: string, input?
98
114
  export declare function reopenWorkItem(coordRoot: string, workId: string, input?: {
99
115
  actor?: string;
100
116
  reason?: string;
117
+ findings?: readonly string[];
101
118
  }): WorkRecord;
119
+ /** Findings raised by the most recent reopen that no later acceptance disposed
120
+ * of. Empty when the work was never reopened with findings. */
121
+ export declare function openOperatorFindings(coordRoot: string, workId: string): {
122
+ actor: string;
123
+ findings: WorkOperatorFinding[];
124
+ };
102
125
  export declare function assertWorkId(workId: string): void;
103
126
  export declare function newWorkId(): string;
104
127
  export declare function newWorkflowRunId(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/work/state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/work/state.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import { hostname } from "node:os";
4
4
  import { dirname, isAbsolute, join, resolve } from "node:path";
5
5
  import { readWorkflowApproval } from "../workflow/approvals.js";
6
6
  import { stableDigest } from "../workflow/durable-record.js";
7
+ import { WORKFLOW_JOURNAL_EVENT_BYTES, workflowJournalPath } from "../workflow/journal.js";
7
8
  import { readWorkflowProof } from "../workflow/proof.js";
8
9
  import { readWorkflowRunManifest, workflowScriptDigest } from "../workflow/run-state.js";
9
10
  import { isWorkspaceBoundExecutionEvidence } from "../workflow/workspaces/validate.js";
@@ -142,6 +143,24 @@ export function cancelWorkItem(coordRoot, workId, input = {}) {
142
143
  export function reopenWorkItem(coordRoot, workId, input = {}) {
143
144
  return appendGovernanceEvent(coordRoot, workId, "work.reopened", ["blocked", "in_review", "succeeded", "cancelled"], input);
144
145
  }
146
+ /** Findings raised by the most recent reopen that no later acceptance disposed
147
+ * of. Empty when the work was never reopened with findings. */
148
+ export function openOperatorFindings(coordRoot, workId) {
149
+ const events = readWorkEvents(coordRoot, workId);
150
+ let actor = "operator";
151
+ let open = [];
152
+ for (const event of events) {
153
+ if (event.event === "work.reopened" && event.findings?.length) {
154
+ actor = event.actor;
155
+ open = event.findings.map((finding) => ({ ...finding }));
156
+ }
157
+ if (event.event === "work.accepted" && event.dispositions?.length) {
158
+ const disposed = new Set(event.dispositions.map((entry) => entry.id));
159
+ open = open.filter((finding) => !disposed.has(finding.id));
160
+ }
161
+ }
162
+ return { actor, findings: open };
163
+ }
145
164
  export function assertWorkId(workId) {
146
165
  if (!WORK_ID.test(workId))
147
166
  throw new Error(`invalid work id ${JSON.stringify(workId)}`);
@@ -248,6 +267,14 @@ function deriveWorkProjection(coordRoot, intent, events, ignoreLease = false) {
248
267
  };
249
268
  }
250
269
  const attemptsRemaining = intent.max_attempts - attempts.length;
270
+ if (latest.status === "journal_unreadable") {
271
+ return {
272
+ ...base,
273
+ state: "blocked",
274
+ reason: `workflow attempt ${latest.number} journal is unreadable: ${latest.journal_error ?? "unknown journal read error"}`,
275
+ next_action: attemptsRemaining > 0 ? "retry" : "none",
276
+ };
277
+ }
251
278
  return {
252
279
  ...base,
253
280
  state: "blocked",
@@ -309,22 +336,34 @@ function inspectAttempt(coordRoot, event, intent, priorRunId) {
309
336
  : "failed";
310
337
  return attempt;
311
338
  }
312
- const journalPath = join(coordRoot, ".harnery", "workflows", runId, "journal.jsonl");
339
+ const journalPath = workflowJournalPath(coordRoot, runId);
313
340
  if (existsSync(journalPath)) {
314
341
  let parked;
315
342
  let resumed = false;
316
- for (const line of readBoundedLines(journalPath, MAX_EVENTS_BYTES, "workflow journal")) {
317
- const value = parseObject(line, `workflow run ${runId} journal`);
318
- if (value.event === "run.parked" && typeof value.approval_id === "string") {
319
- parked = value.approval_id;
320
- resumed = false;
343
+ try {
344
+ for (const line of readBoundedLines(journalPath, MAX_EVENTS_BYTES, WORKFLOW_JOURNAL_EVENT_BYTES, "workflow journal")) {
345
+ const value = parseObject(line, `workflow run ${runId} journal`);
346
+ if (value.event === "run.parked" && typeof value.approval_id === "string") {
347
+ parked = value.approval_id;
348
+ resumed = false;
349
+ }
350
+ if (value.event === "run.resume")
351
+ resumed = true;
352
+ }
353
+ if (parked && !resumed) {
354
+ attempt.status = "parked";
355
+ attempt.approval_id = parked;
356
+ return attempt;
321
357
  }
322
- if (value.event === "run.resume")
323
- resumed = true;
324
358
  }
325
- if (parked && !resumed) {
326
- attempt.status = "parked";
327
- attempt.approval_id = parked;
359
+ catch (error) {
360
+ if (workflowResumeLeaseIsLive(coordRoot, runId)) {
361
+ attempt.status = "running";
362
+ }
363
+ else {
364
+ attempt.status = "journal_unreadable";
365
+ attempt.journal_error = boundedJournalError(error.message);
366
+ }
328
367
  return attempt;
329
368
  }
330
369
  }
@@ -354,11 +393,17 @@ function appendGovernanceEvent(coordRoot, workId, event, allowed, input) {
354
393
  const acceptance = event === "work.accepted"
355
394
  ? acceptanceAuthority(coordRoot, current, input.acceptedUnknowns ?? [])
356
395
  : undefined;
396
+ const dispositions = event === "work.accepted"
397
+ ? findingAuthority(coordRoot, workId, input.dispositions ?? [])
398
+ : undefined;
399
+ const findings = event === "work.reopened" ? normalizeRaisedFindings(input.findings) : undefined;
357
400
  appendWorkEventUnderLease(coordRoot, workId, {
358
401
  event,
359
402
  actor: boundedActor(input.actor),
360
403
  reason: boundedOptional(input.reason, "work reason", MAX_REASON),
361
404
  ...acceptance,
405
+ ...(findings ? { findings } : {}),
406
+ ...(dispositions ? { dispositions } : {}),
362
407
  });
363
408
  }
364
409
  finally {
@@ -367,6 +412,51 @@ function appendGovernanceEvent(coordRoot, workId, event, allowed, input) {
367
412
  }
368
413
  return reconcileWorkItem(coordRoot, workId, input.actor);
369
414
  }
415
+ /** Acceptance fails closed while an operator finding is open. The operator who
416
+ * raised it decides whether it was addressed; the team cannot mark its own. */
417
+ function findingAuthority(coordRoot, workId, dispositions) {
418
+ const open = openOperatorFindings(coordRoot, workId).findings;
419
+ if (open.length === 0) {
420
+ if (dispositions.length > 0) {
421
+ throw new Error(`work item ${workId} has no open operator finding to dispose`);
422
+ }
423
+ return [];
424
+ }
425
+ const byId = new Map(dispositions.map((entry) => [entry.id, entry]));
426
+ const undisposed = open.filter((finding) => !byId.has(finding.id));
427
+ if (undisposed.length > 0) {
428
+ throw new Error(`work item ${workId} cannot be accepted while operator findings are undisposed: ${undisposed
429
+ .map((finding) => finding.id)
430
+ .join(", ")}`);
431
+ }
432
+ const known = new Set(open.map((finding) => finding.id));
433
+ return dispositions.map((entry) => {
434
+ if (!known.has(entry.id)) {
435
+ throw new Error(`work item ${workId} has no open operator finding ${entry.id}`);
436
+ }
437
+ if (entry.outcome !== "fixed" && entry.outcome !== "deferred") {
438
+ throw new Error(`operator finding ${entry.id} disposition must be fixed or deferred`);
439
+ }
440
+ if (entry.outcome === "deferred" && !entry.reason?.trim()) {
441
+ throw new Error(`deferring operator finding ${entry.id} requires a reason`);
442
+ }
443
+ return {
444
+ id: entry.id,
445
+ outcome: entry.outcome,
446
+ ...(entry.reason ? { reason: boundedString(entry.reason, "disposition reason", 500) } : {}),
447
+ };
448
+ });
449
+ }
450
+ function normalizeRaisedFindings(statements) {
451
+ if (!statements || statements.length === 0)
452
+ return undefined;
453
+ if (statements.length > 20)
454
+ throw new Error("a reopen may raise at most 20 findings");
455
+ return statements.map((statement, index) => ({
456
+ id: `f${index + 1}`,
457
+ statement: boundedString(statement, `finding[${index}] statement`, 1_000),
458
+ }));
459
+ }
370
460
  function acceptanceAuthority(coordRoot, current, acceptedUnknowns) {
371
461
  const latest = current.projection.attempts.at(-1);
372
462
  if (!latest?.proof_path)
@@ -484,7 +574,7 @@ function readWorkEvents(coordRoot, workId) {
484
574
  const path = join(workDir(coordRoot, workId), "events.jsonl");
485
575
  if (!existsSync(path))
486
576
  return [];
487
- const events = readBoundedLines(path, MAX_EVENTS_BYTES, `work item ${workId} events`).map((line, index) => {
577
+ const events = readBoundedLines(path, MAX_EVENTS_BYTES, MAX_EVENT_BYTES, `work item ${workId} events`).map((line, index) => {
488
578
  const event = parseObject(line, `work item ${workId} event ${index + 1}`);
489
579
  validateWorkEvent(event, workId, index + 1);
490
580
  return event;
@@ -756,7 +846,7 @@ function writePrivateJson(path, value, maxBytes) {
756
846
  writeFileSync(path, body, { encoding: "utf8", flag: "wx", mode: 0o600 });
757
847
  chmodSync(path, 0o600);
758
848
  }
759
- function readBoundedLines(path, maxBytes, field) {
849
+ function readBoundedLines(path, maxBytes, maxRecordBytes, field) {
760
850
  const size = statSync(path).size;
761
851
  if (size > maxBytes)
762
852
  throw new Error(`${field} exceeds ${maxBytes} bytes`);
@@ -767,11 +857,15 @@ function readBoundedLines(path, maxBytes, field) {
767
857
  if (lines.length > MAX_EVENTS)
768
858
  throw new Error(`${field} exceeds ${MAX_EVENTS} records`);
769
859
  for (const line of lines) {
770
- if (Buffer.byteLength(line) > MAX_EVENT_BYTES)
860
+ if (Buffer.byteLength(line) > maxRecordBytes)
771
861
  throw new Error(`${field} contains an oversized record`);
772
862
  }
773
863
  return lines;
774
864
  }
865
+ function boundedJournalError(message) {
866
+ const normalized = message.trim() || "unknown journal read error";
867
+ return normalized.length > MAX_REASON ? normalized.slice(0, MAX_REASON) : normalized;
868
+ }
775
869
  function parseObject(input, field) {
776
870
  try {
777
871
  const value = JSON.parse(input);
@@ -1 +1 @@
1
- {"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/approvals.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/approvals.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { chmodSync, existsSync, linkSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
+ import { appendWorkflowJournalEvent } from "./journal.js";
4
5
  export const WORKFLOW_APPROVAL_SCHEMA_VERSION = 1;
5
6
  const RECORD_LIMIT_BYTES = 128 * 1024;
6
7
  const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
@@ -136,19 +137,15 @@ function appendResolutionJournal(coordRoot, approval) {
136
137
  const journalPath = join(coordRoot, ".harnery", "workflows", approval.request.run_id, "journal.jsonl");
137
138
  if (!existsSync(journalPath) || !approval.decision)
138
139
  return;
139
- const event = {
140
- schema_version: 1,
141
- run_id: approval.request.run_id,
140
+ appendWorkflowJournalEvent(coordRoot, approval.request.run_id, "approval.resolved", {
142
141
  ts: approval.decision.decided_at,
143
- event: "approval.resolved",
144
142
  stage: "",
145
143
  approval_id: approval.request.id,
146
144
  decision_id: approval.request.decision_id,
147
145
  verdict: approval.decision.verdict,
148
146
  actor: approval.decision.actor,
149
147
  reason: approval.decision.reason ?? null,
150
- };
151
- writeFileSync(journalPath, `${JSON.stringify(event)}\n`, { encoding: "utf8", flag: "a" });
148
+ });
152
149
  }
153
150
  function approvalRequestDigest(policySha256, request, evaluation) {
154
151
  return createHash("sha256")
@@ -1 +1 @@
1
- {"version":3,"file":"attempt-context.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/attempt-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,sBAAsB,EAIvB,MAAM,YAAY,CAAC;AAoBpB,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,CA8BtF;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,sBAAsB,CAAC,CAU7F;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,sBAAsB,CAOjG"}
1
+ {"version":3,"file":"attempt-context.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/attempt-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,sBAAsB,EAKvB,MAAM,YAAY,CAAC;AAyBpB,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,CAiCtF;AAmCD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,sBAAsB,CAAC,CAc7F;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,sBAAsB,CAOjG"}