lazycodex-ai 5.0.0-beta.53 → 5.0.0-beta.55

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 (80) hide show
  1. package/dist/cli/index.js +53 -19
  2. package/dist/cli-node/index.js +53 -19
  3. package/package.json +1 -1
  4. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  5. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  6. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  7. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  8. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  9. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  10. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  12. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  14. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  15. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  16. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  17. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  19. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  21. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  22. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  23. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  25. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +5 -1
  27. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +14 -34
  28. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +1 -4
  29. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.d.ts +0 -18
  30. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.js +1 -88
  31. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.d.ts +2 -0
  32. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +10 -3
  33. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +2 -2
  34. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +2 -0
  35. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +86 -174
  36. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +2 -18
  37. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +23 -33
  38. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.d.ts +2 -0
  39. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +16 -9
  40. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  41. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +18 -59
  43. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +1 -6
  44. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-reconciliation.ts +1 -124
  45. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +11 -3
  46. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-arg-parser.ts +10 -4
  47. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +2 -0
  48. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +31 -50
  49. package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +29 -10
  50. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-final.test.ts +0 -82
  51. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint.test.ts +16 -22
  52. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-checkpoint.test.ts +27 -0
  53. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-json-errors.test.ts +25 -0
  54. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-snapshot.test.ts +34 -11
  55. package/packages/omo-codex/plugin/components/ulw-loop/test/guided-recovery.test.ts +9 -47
  56. package/packages/omo-codex/plugin/components/ulw-loop/test/review-blockers.test.ts +9 -7
  57. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  78. package/packages/omo-codex/plugin/package-lock.json +12 -12
  79. package/packages/omo-codex/plugin/package.json +1 -1
  80. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
@@ -1,14 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // components/ulw-loop/src/checkpoint.ts
4
- import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
5
- import { readFile as readFile5 } from "node:fs/promises";
4
+ import { existsSync as existsSync3, statSync as statSync2 } from "node:fs";
5
+ import { readFile as readFile4 } from "node:fs/promises";
6
6
  import { resolve as resolve3 } from "node:path";
7
7
 
8
- // components/ulw-loop/src/checkpoint-reconciliation.ts
9
- import { existsSync as existsSync2 } from "node:fs";
10
- import { readFile as readFile2 } from "node:fs/promises";
11
-
12
8
  // components/ulw-loop/src/codex-goal-snapshot.ts
13
9
  import { existsSync } from "node:fs";
14
10
  import { readFile } from "node:fs/promises";
@@ -20,16 +16,25 @@ function safeObject(value) {
20
16
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
21
17
  }
22
18
  function safeString(value) {
19
+ return typeof value === "string" ? value : "";
20
+ }
21
+ function safeStatusString(value) {
23
22
  return typeof value === "string" ? value.trim() : "";
24
23
  }
25
24
  function normalizeStatus(value) {
26
- const status = safeString(value).toLowerCase();
25
+ const status = safeStatusString(value).toLowerCase();
27
26
  if (status === "complete" || status === "completed" || status === "done")
28
27
  return "complete";
29
28
  if (status === "cancelled" || status === "canceled")
30
29
  return "cancelled";
31
30
  if (status === "failed" || status === "failure")
32
31
  return "failed";
32
+ if (status === "paused")
33
+ return "paused";
34
+ if (status === "usage_limited")
35
+ return "usage_limited";
36
+ if (status === "budget_limited")
37
+ return "budget_limited";
33
38
  if (status === "active" || status === "in_progress" || status === "pending" || status === "running")
34
39
  return "active";
35
40
  return "unknown";
@@ -75,46 +80,32 @@ function reconcileCodexGoalSnapshot(snapshot, options) {
75
80
  const effectiveSnapshot = snapshot ?? { available: false, raw: null };
76
81
  const errors = [];
77
82
  const warnings = [];
83
+ const expected = options.expectedObjective;
84
+ const normalizedExpected = normalizeObjective(expected);
78
85
  if (!effectiveSnapshot.available) {
79
- const message = "Codex goal snapshot is absent or reports no active goal; call get_goal and pass its JSON with --codex-goal-json.";
80
- if (options.requireSnapshot)
81
- errors.push(message);
82
- else
83
- warnings.push(message);
86
+ warnings.push(`call get_goal; if none, create_goal with codexObjective "${expected}" verbatim`);
84
87
  return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
85
88
  }
86
- const expected = normalizeObjective(options.expectedObjective);
87
- const accepted = new Set([expected, ...(options.acceptedObjectives ?? []).map((objective) => normalizeObjective(objective))].filter(Boolean));
89
+ const accepted = new Set([
90
+ normalizedExpected,
91
+ ...(options.acceptedObjectives ?? []).map((objective) => normalizeObjective(objective))
92
+ ].filter(Boolean));
88
93
  const actual = normalizeObjective(effectiveSnapshot.objective ?? "");
89
- if (!actual) {
90
- errors.push("Codex goal snapshot is missing objective text.");
91
- } else if (!accepted.has(actual)) {
92
- errors.push(`Codex goal objective mismatch: expected "${expected}", got "${actual}".`);
94
+ if (actual && !accepted.has(normalizeObjective(actual))) {
95
+ warnings.push(`driver_objective_differs: expected "${expected}", got "${actual}".`);
93
96
  }
94
- const allowed = options.allowedStatuses ?? (options.requireComplete ? ["complete"] : ["active", "complete"]);
95
97
  const actualStatus = effectiveSnapshot.status ?? "unknown";
96
- if (!allowed.includes(actualStatus)) {
97
- errors.push(`Codex goal status mismatch: expected ${allowed.join(" or ")}, got ${actualStatus}.`);
98
- }
99
- if (options.requireComplete && actualStatus !== "complete") {
100
- errors.push('Codex goal is not complete; call update_goal({status: "complete"}) only after the objective is actually complete, then pass the fresh get_goal JSON.');
98
+ if (actualStatus === "paused" || actualStatus === "usage_limited" || actualStatus === "budget_limited") {
99
+ warnings.push("/goal resume or raise the budget");
101
100
  }
101
+ if (actualStatus === "complete")
102
+ warnings.push(`driver closed early: call create_goal with codexObjective "${expected}" verbatim`);
102
103
  return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
103
104
  }
104
105
  function formatCodexGoalReconciliation(reconciliation) {
105
106
  const parts = [...reconciliation.errors, ...reconciliation.warnings];
106
107
  return parts.join(" ");
107
108
  }
108
- function codexGoalMismatchRecovery(expectedObjective, snapshot) {
109
- const receivedObjective = snapshot?.objective ?? "";
110
- const message = [
111
- "Recovery: the Codex goal objective must equal the plan's codexObjective exactly — copy the expected value below into update_goal and re-run get_goal.",
112
- `expected codexObjective: ${expectedObjective}`,
113
- `received objective: ${receivedObjective || "(none)"}`
114
- ].join(`
115
- `);
116
- return { message, details: { expectedObjective, receivedObjective } };
117
- }
118
109
 
119
110
  // components/ulw-loop/src/paths.ts
120
111
  import { isAbsolute, join, relative, sep } from "node:path";
@@ -308,114 +299,21 @@ function hasEssentialCriteriaPass(goal) {
308
299
  return criteria.length > 0 && criteria.every((criterion) => criterion.status === "pass");
309
300
  }
310
301
 
311
- // components/ulw-loop/src/checkpoint-reconciliation.ts
312
- function codexSnapshotMismatchError(input) {
313
- const { reconciliation, snapshot, taskScopedHint } = input;
314
- const hint = taskScopedHint?.aggregate === true && snapshot?.status === "complete" && snapshot.objective !== undefined ? buildTaskScopedAggregateReconciliationHint(taskScopedHint.goal, taskScopedHint.final) : "";
315
- const recovery = codexGoalMismatchRecovery(input.expectedObjective, snapshot);
316
- return new UlwLoopError(`${formatCodexGoalReconciliation(reconciliation)}${hint}
317
- ${recovery.message}`, "ulw_loop_codex_snapshot_mismatch", { details: recovery.details });
318
- }
319
- function normalizeObjective2(value) {
320
- return value.replace(/\s+/g, " ").trim();
321
- }
322
- function textMentionsUlwLoopPlanArtifact(value) {
323
- const normalized = (value ?? "").toLowerCase();
324
- return normalized.includes(ULW_LOOP_DIR.toLowerCase()) || normalized.includes(ULW_LOOP_GOALS.toLowerCase()) || normalized.includes(ULW_LOOP_LEDGER.toLowerCase());
325
- }
326
- function textMentionsGoalId(value, goalId) {
327
- return (value ?? "").toLowerCase().includes(goalId.toLowerCase());
328
- }
329
- function textHasCompletionValidationEvidence(value) {
330
- const normalized = (value ?? "").toLowerCase();
331
- const done = /\b(?:planned work|implementation|deliverables?|scope|task|work)\b/.test(normalized) && /\b(?:done|complete|completed|finished|shipped)\b/.test(normalized);
332
- const verified = /\b(?:validation|verification|tests?|build|lint|review|quality gate|code-review)\b/.test(normalized) && /\b(?:passed|complete|completed|clean|green|approve|approved|clear)\b/.test(normalized);
333
- return done && verified;
334
- }
335
- async function snapshotObjectiveMapsToUlwLoopPlan(repoRoot, snapshotObjective, scope) {
336
- const actual = normalizeObjective2(snapshotObjective).toLowerCase();
337
- if (textMentionsUlwLoopPlanArtifact(actual))
338
- return true;
339
- if (actual.length < 24 || !existsSync2(ulwLoopBriefPath(repoRoot, scope)))
340
- return false;
341
- try {
342
- const brief = normalizeObjective2(await readFile2(ulwLoopBriefPath(repoRoot, scope), "utf8")).toLowerCase();
343
- return brief.length >= 24 && (brief.includes(actual) || actual.includes(brief));
344
- } catch (error) {
345
- if (error instanceof Error)
346
- return false;
347
- throw error;
348
- }
349
- }
350
- async function canReconcileCompletedTaskScopedAggregateSnapshot(repoRoot, plan, goal, snapshotObjective, evidence, scope) {
351
- if (codexGoalMode(plan) !== "aggregate")
352
- return false;
353
- if (goal.status !== "in_progress" || plan.activeGoalId !== goal.id)
354
- return false;
355
- if (isFinalRunCompletionCandidate(plan, goal)) {
356
- return snapshotObjectiveMapsToUlwLoopPlan(repoRoot, snapshotObjective, scope);
357
- }
358
- if (!textMentionsUlwLoopPlanArtifact(evidence) || !textMentionsGoalId(evidence, goal.id))
359
- return false;
360
- if (!textHasCompletionValidationEvidence(evidence))
361
- return false;
362
- return snapshotObjectiveMapsToUlwLoopPlan(repoRoot, snapshotObjective, scope);
363
- }
364
- async function canReconcileActiveFinalTaskScopedAggregateSnapshot(repoRoot, plan, goal, snapshotObjective, evidence, scope) {
365
- if (codexGoalMode(plan) !== "aggregate")
366
- return false;
367
- if (goal.status !== "in_progress" || plan.activeGoalId !== goal.id)
368
- return false;
369
- if (!isFinalRunCompletionCandidate(plan, goal))
370
- return false;
371
- if (!textHasCompletionValidationEvidence(evidence))
372
- return false;
373
- return snapshotObjectiveMapsToUlwLoopPlan(repoRoot, snapshotObjective, scope);
374
- }
375
- function buildCompletedLegacyGoalRemediation(goal) {
376
- return [
377
- "If get_goal returns a different completed legacy/thread objective, do not repeat --status complete in this thread.",
378
- `Record a non-terminal blocker with: omo-agent-toolkit ulw-loop checkpoint --goal-id ${goal.id} --status blocked --evidence "<completed legacy Codex goal blocks create_goal in this thread>" --codex-goal-json "<different completed get_goal JSON or path>".`,
379
- "Then continue only from a Codex goal context with no active/completed conflicting goal, in the same repo/worktree, and create the intended goal there."
380
- ].join(" ");
381
- }
382
- function buildTaskScopedAggregateReconciliationHint(goal, final) {
383
- if (final) {
384
- return ` Final task-scoped aggregate reconciliation requires the checkpoint goal to be the active in-progress final OMO goal and the completed get_goal objective to map to the ulw-loop brief or artifact. ${buildCompletedLegacyGoalRemediation(goal)}`;
385
- }
386
- return ` Completed task-scoped aggregate reconciliation requires the checkpoint goal to be the active in-progress OMO goal, evidence that names that active OMO goal id, names .omo/ulw-loop/goals.json or ledger.jsonl, includes completed implementation plus validation/review evidence, and a get_goal objective that maps to the ulw-loop brief/artifact. ${buildCompletedLegacyGoalRemediation(goal)}`;
387
- }
388
-
389
302
  // components/ulw-loop/src/checkpoint-codex-validation.ts
390
- function normalizeObjective3(value) {
391
- return value.replace(/\s+/g, " ").trim();
392
- }
393
303
  async function validateCheckpointCodexGoal(input) {
394
- const aggregate = codexGoalMode(input.plan) === "aggregate";
395
- const final = isFinalRunCompletionCandidate(input.plan, input.goal);
396
304
  const snapshot = await readCodexGoalSnapshotInput(input.raw, input.repoRoot);
397
- const expectedObjective = expectedCodexObjective(input.plan, input.goal);
305
+ const expected = expectedCodexObjective(input.plan, input.goal);
398
306
  const reconciliation = reconcileCodexGoalSnapshot(snapshot, {
399
- expectedObjective,
400
- ...aggregate ? { acceptedObjectives: compatibleCodexObjectives(input.plan) } : {},
401
- allowedStatuses: aggregate ? final ? ["complete"] : ["active"] : ["complete"],
402
- requireSnapshot: true,
403
- requireComplete: !aggregate || final
404
- });
405
- if (reconciliation.ok)
406
- return reconciliation.snapshot.raw;
407
- const objective = snapshot?.objective;
408
- const mismatchedTaskObjective = snapshot?.available === true && objective !== undefined && normalizeObjective3(objective) !== normalizeObjective3(expectedObjective);
409
- const completedTaskScoped = mismatchedTaskObjective && snapshot.status === "complete" && await canReconcileCompletedTaskScopedAggregateSnapshot(input.repoRoot, input.plan, input.goal, objective, input.evidence, input.scope);
410
- const activeFinalTaskScoped = mismatchedTaskObjective && snapshot.status === "active" && await canReconcileActiveFinalTaskScopedAggregateSnapshot(input.repoRoot, input.plan, input.goal, objective, input.evidence, input.scope);
411
- if (completedTaskScoped || activeFinalTaskScoped)
412
- return reconciliation.snapshot.raw;
413
- throw codexSnapshotMismatchError({
414
- reconciliation,
415
- snapshot,
416
- expectedObjective,
417
- taskScopedHint: { goal: input.goal, aggregate, final }
307
+ expectedObjective: expected,
308
+ ...codexGoalMode(input.plan) === "aggregate" ? { acceptedObjectives: compatibleCodexObjectives(input.plan) } : {}
418
309
  });
310
+ if (!reconciliation.ok)
311
+ throw new CodexGoalSnapshotError(formatCodexGoalReconciliation(reconciliation));
312
+ return {
313
+ raw: snapshot?.raw,
314
+ nextActions: reconciliation.warnings,
315
+ warnings: reconciliation.warnings.filter((warning) => warning.startsWith("driver_objective_differs"))
316
+ };
419
317
  }
420
318
  function combineCheckpointValidationErrors(codexError, gateError) {
421
319
  return new UlwLoopError(`${codexError.message}
@@ -427,7 +325,7 @@ ${gateError.message}`, "ULW_LOOP_QUALITY_GATE_INVALID", {
427
325
  // components/ulw-loop/src/plan-io.ts
428
326
  import { AsyncLocalStorage } from "node:async_hooks";
429
327
  import { createReadStream, readdirSync } from "node:fs";
430
- import { appendFile, mkdir, readFile as readFile3, rename, writeFile } from "node:fs/promises";
328
+ import { appendFile, mkdir, readFile as readFile2, rename, writeFile } from "node:fs/promises";
431
329
  import { createInterface } from "node:readline";
432
330
 
433
331
  // components/ulw-loop/src/plan-missing-recovery.ts
@@ -694,7 +592,7 @@ async function readUlwLoopPlan(repoRoot, scope) {
694
592
  const path = ulwLoopGoalsPath(repoRoot, scope);
695
593
  let raw;
696
594
  try {
697
- raw = await readFile3(path, "utf8");
595
+ raw = await readFile2(path, "utf8");
698
596
  } catch (error) {
699
597
  if (!hasCode2(error, "ENOENT"))
700
598
  throw error;
@@ -1113,7 +1011,7 @@ function adversarialVerdict(row, field) {
1113
1011
  }
1114
1012
 
1115
1013
  // components/ulw-loop/src/surface.ts
1116
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
1014
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
1117
1015
  import { dirname as dirname2, join as join2 } from "node:path";
1118
1016
  import { fileURLToPath } from "node:url";
1119
1017
  var REVIEWER_ROLES_BY_SURFACE = {
@@ -1173,7 +1071,7 @@ function resolveToolkitSurface(options) {
1173
1071
  }
1174
1072
  function readMarkerSurface(markerPath) {
1175
1073
  try {
1176
- if (!existsSync3(markerPath))
1074
+ if (!existsSync2(markerPath))
1177
1075
  return null;
1178
1076
  const parsed = JSON.parse(readFileSync2(markerPath, "utf8"));
1179
1077
  return parseSurface(parsed["surface"]);
@@ -1376,7 +1274,7 @@ function parseAdversarialCases(value, byId) {
1376
1274
  }
1377
1275
 
1378
1276
  // components/ulw-loop/src/cli-arg-parser.ts
1379
- import { readFile as readFile4 } from "node:fs/promises";
1277
+ import { readFile as readFile3 } from "node:fs/promises";
1380
1278
  var VALUE_FLAGS = new Set("--brief --brief-file --session-id --codex-goal-mode --validation-batch-json --goal --goal-id --criterion-id --status --evidence --notes --codex-goal-json --quality-gate-json --kind --rationale --title --objective --target-goal-id --source --after-json --directive-json --directive-file --idempotency-key --proposals-json".split(" "));
1381
1279
  var SUBCOMMANDS = new Set("create-goals status complete-goals criteria record-evidence checkpoint steer add-goal record-review-blockers".split(" "));
1382
1280
  function hasFlag(argv, flag) {
@@ -1424,7 +1322,7 @@ async function readJsonInput(value) {
1424
1322
  if (value === undefined)
1425
1323
  return;
1426
1324
  try {
1427
- return JSON.parse(looksLikeJson(value) ? value : await readFile4(value, "utf8"));
1325
+ return JSON.parse(looksLikeJson(value) ? value : await readFile3(value, "utf8"));
1428
1326
  } catch (error) {
1429
1327
  const message = error instanceof Error ? error.message : "unknown error";
1430
1328
  throw new UlwLoopError(`Invalid JSON input: ${message}`, "ULW_LOOP_JSON_INPUT_INVALID", { cause: error });
@@ -1433,13 +1331,13 @@ async function readJsonInput(value) {
1433
1331
  async function parseCodexGoalJson(value) {
1434
1332
  if (value === undefined)
1435
1333
  return;
1436
- const raw = looksLikeJson(value) ? value : await readFile4(value, "utf8");
1437
1334
  try {
1335
+ const raw = looksLikeJson(value) ? value : await readFile3(value, "utf8");
1438
1336
  JSON.parse(raw);
1439
1337
  return raw;
1440
1338
  } catch (error) {
1441
1339
  const message = error instanceof Error ? error.message : "unknown error";
1442
- throw new UlwLoopError(`Invalid --codex-goal-json: ${message}`, "ULW_LOOP_CODEX_GOAL_JSON_INVALID", { cause: error });
1340
+ throw new UlwLoopError(`Invalid --codex-goal-json: ${looksLikeJson(value) ? message : "neither valid JSON nor a readable path"}`, "ULW_LOOP_CODEX_GOAL_JSON_INVALID", { cause: error });
1443
1341
  }
1444
1342
  }
1445
1343
  function required(argv, flag, code) {
@@ -1585,7 +1483,7 @@ function fail(message, code = "ULW_LOOP_VALIDATION_BATCH_INVALID") {
1585
1483
  }
1586
1484
 
1587
1485
  // components/ulw-loop/src/checkpoint.ts
1588
- var QUALITY_GATE_FS = { existsSync: existsSync4, statSync: statSync2 };
1486
+ var QUALITY_GATE_FS = { existsSync: existsSync3, statSync: statSync2 };
1589
1487
  function ulwLoopFail2(message, code) {
1590
1488
  throw new UlwLoopError(message, code);
1591
1489
  }
@@ -1608,10 +1506,10 @@ async function readJsonInput2(raw, repoRoot) {
1608
1506
  throw error;
1609
1507
  }
1610
1508
  const path = resolve3(repoRoot, trimmed);
1611
- if (!existsSync4(path))
1509
+ if (!existsSync3(path))
1612
1510
  return ulwLoopFail2("Quality gate JSON is neither valid JSON nor a readable path.", "ulw_loop_json_input_invalid");
1613
1511
  try {
1614
- return JSON.parse(await readFile5(path, "utf8"));
1512
+ return JSON.parse(await readFile4(path, "utf8"));
1615
1513
  } catch (error) {
1616
1514
  return ulwLoopFail2(`Quality gate path does not contain valid JSON${error instanceof Error ? `: ${error.message}` : "."}`, "ulw_loop_json_input_invalid");
1617
1515
  }
@@ -1680,6 +1578,8 @@ async function checkpointUlwLoop(repoRoot, args, scope) {
1680
1578
  let aggregateCompletion;
1681
1579
  let qualityGate;
1682
1580
  let codexGoal;
1581
+ let nextActions = [];
1582
+ let warnings = [];
1683
1583
  if (args.status === "complete") {
1684
1584
  const aggregate = codexGoalMode(plan) === "aggregate";
1685
1585
  const final = isFinalRunCompletionCandidate(plan, goal);
@@ -1694,7 +1594,7 @@ async function checkpointUlwLoop(repoRoot, args, scope) {
1694
1594
  requireAllCriteriaPass(goal);
1695
1595
  let codexValidationError;
1696
1596
  try {
1697
- codexGoal = await validateCheckpointCodexGoal({
1597
+ const validation = await validateCheckpointCodexGoal({
1698
1598
  repoRoot,
1699
1599
  plan,
1700
1600
  goal,
@@ -1702,6 +1602,9 @@ async function checkpointUlwLoop(repoRoot, args, scope) {
1702
1602
  evidence,
1703
1603
  ...scope === undefined ? {} : { scope }
1704
1604
  });
1605
+ codexGoal = validation.raw;
1606
+ nextActions = validation.nextActions;
1607
+ warnings = validation.warnings;
1705
1608
  } catch (error) {
1706
1609
  if (!(error instanceof UlwLoopError))
1707
1610
  throw error;
@@ -1743,6 +1646,8 @@ async function checkpointUlwLoop(repoRoot, args, scope) {
1743
1646
  goal.updatedAt = now;
1744
1647
  if (aggregateCompletion !== undefined)
1745
1648
  plan.aggregateCompletion = aggregateCompletion;
1649
+ if (aggregateCompletion !== undefined)
1650
+ nextActions = [...nextActions, 'aggregate complete — now update_goal({status:"complete"})'];
1746
1651
  plan.updatedAt = now;
1747
1652
  await writePlan(repoRoot, plan, scope);
1748
1653
  const ledgerEntry = buildLedger(now, args, goal, qualityGate, codexGoal, aggregateCompletion);
@@ -1750,7 +1655,7 @@ async function checkpointUlwLoop(repoRoot, args, scope) {
1750
1655
  const closedBatch = args.status === "complete" ? batchClosedBy(plan, goal.id) : undefined;
1751
1656
  if (closedBatch !== undefined)
1752
1657
  await appendLedger(repoRoot, { at: now, kind: "batch_closed", goalId: goal.id, message: closedBatch.batchId }, scope);
1753
- return aggregateCompletion === undefined ? { plan, goal, ledgerEntry } : { plan, goal, ledgerEntry, aggregateCompletion };
1658
+ return aggregateCompletion === undefined ? { plan, goal, ledgerEntry, nextActions, warnings } : { plan, goal, ledgerEntry, aggregateCompletion, nextActions, warnings };
1754
1659
  });
1755
1660
  }
1756
1661
 
@@ -2085,14 +1990,14 @@ function joinLines(lines) {
2085
1990
  }
2086
1991
 
2087
1992
  // components/ulw-loop/src/plan-crud.ts
2088
- import { existsSync as existsSync5 } from "node:fs";
1993
+ import { existsSync as existsSync4 } from "node:fs";
2089
1994
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
2090
1995
 
2091
1996
  // components/ulw-loop/src/plan-goal-factory.ts
2092
1997
  function cleanLine(line) {
2093
1998
  return line.replace(/^\s*(?:[-*+]\s+|\d+[.)]\s+)/, "").trim();
2094
1999
  }
2095
- function normalizeObjective4(value) {
2000
+ function normalizeObjective2(value) {
2096
2001
  return value.replace(/\s+/g, " ").trim();
2097
2002
  }
2098
2003
  function titleFromObjective(objective, fallback) {
@@ -2113,7 +2018,7 @@ function truncateObjective(objective) {
2113
2018
  return objective.length > 80 ? `${objective.slice(0, 77).trimEnd()}...` : objective;
2114
2019
  }
2115
2020
  function seedDefaultSuccessCriteria(goalIndex, objective) {
2116
- const subject = truncateObjective(normalizeObjective4(objective) || `Goal ${goalIndex + 1}`);
2021
+ const subject = truncateObjective(normalizeObjective2(objective) || `Goal ${goalIndex + 1}`);
2117
2022
  const rows = [
2118
2023
  [
2119
2024
  "C001",
@@ -2148,8 +2053,8 @@ function seedDefaultSuccessCriteria(goalIndex, objective) {
2148
2053
  }));
2149
2054
  }
2150
2055
  function deriveGoalCandidates(brief) {
2151
- const bulletGoals = brief.split(/\r?\n/).map((line) => ({ original: line, cleaned: normalizeObjective4(cleanLine(line)) })).filter(({ cleaned }) => cleaned.length > 0 && cleaned.length <= 1200).filter(({ original, cleaned }, index, all) => /^\s*(?:[-*+]\s+|\d+[.)]\s+)/.test(original) && all.findIndex((candidate) => candidate.cleaned === cleaned) === index).map(({ cleaned }) => cleaned);
2152
- const paragraphs = brief.split(/\n\s*\n/).map(normalizeObjective4).filter((paragraph) => paragraph.length > 0 && !paragraph.startsWith("#"));
2056
+ const bulletGoals = brief.split(/\r?\n/).map((line) => ({ original: line, cleaned: normalizeObjective2(cleanLine(line)) })).filter(({ cleaned }) => cleaned.length > 0 && cleaned.length <= 1200).filter(({ original, cleaned }, index, all) => /^\s*(?:[-*+]\s+|\d+[.)]\s+)/.test(original) && all.findIndex((candidate) => candidate.cleaned === cleaned) === index).map(({ cleaned }) => cleaned);
2057
+ const paragraphs = brief.split(/\n\s*\n/).map(normalizeObjective2).filter((paragraph) => paragraph.length > 0 && !paragraph.startsWith("#"));
2153
2058
  const selected = (bulletGoals.length > 0 ? bulletGoals : paragraphs).length > 0 ? bulletGoals.length > 0 ? bulletGoals : paragraphs : ["Complete the requested project objective."];
2154
2059
  return selected.map((objective, index) => ({
2155
2060
  title: titleFromObjective(objective, `Goal ${index + 1}`),
@@ -2195,7 +2100,7 @@ function clearGoalBlockerFields2(goal) {
2195
2100
  }
2196
2101
  async function createUlwLoopPlan(repoRoot, args, scope) {
2197
2102
  return withUlwLoopMutationLock(repoRoot, scope, async () => {
2198
- if (!args.force && existsSync5(ulwLoopGoalsPath(repoRoot, scope))) {
2103
+ if (!args.force && existsSync4(ulwLoopGoalsPath(repoRoot, scope))) {
2199
2104
  const existing = await readUlwLoopPlan(repoRoot, scope);
2200
2105
  if (isUlwLoopDone(existing))
2201
2106
  throw completedPlanExistsError(scope);
@@ -2327,10 +2232,7 @@ async function checkpoint(repoRoot, argv, json, scope) {
2327
2232
  const goalId = required2(argv, "--goal-id");
2328
2233
  const statusValue = checkpointStatus(required2(argv, "--status"));
2329
2234
  const evidence = required2(argv, "--evidence");
2330
- const codexGoalJson = await parseCodexGoalJson(statusValue === "complete" ? required2(argv, "--codex-goal-json") : readValue(argv, "--codex-goal-json"));
2331
- if (statusValue === "complete" && codexGoalJson === undefined) {
2332
- throw new UlwLoopError("Missing --codex-goal-json.", "ULW_LOOP_CODEX_GOAL_JSON_REQUIRED");
2333
- }
2235
+ const codexGoalJson = await parseCodexGoalJson(readValue(argv, "--codex-goal-json"));
2334
2236
  const qualityGateJson = readValue(argv, "--quality-gate-json");
2335
2237
  const args = {
2336
2238
  goalId,
@@ -2376,7 +2278,7 @@ function checkpointStatus(value) {
2376
2278
  }
2377
2279
 
2378
2280
  // components/ulw-loop/src/cli-subcommands.ts
2379
- import { readFile as readFile6 } from "node:fs/promises";
2281
+ import { readFile as readFile5 } from "node:fs/promises";
2380
2282
 
2381
2283
  // components/ulw-loop/src/cli-steering.ts
2382
2284
  var SOURCES = ["user_prompt_submit", "finding", "cli"];
@@ -2723,11 +2625,12 @@ async function recordFinalReviewBlockers(repoRoot, args, scope) {
2723
2625
  if (!isFinalRunCompletionCandidate(plan, goal))
2724
2626
  ulwLoopError(`${goal.id} is not final.`, "ulw_loop_not_final_story");
2725
2627
  const snapshot = await readCodexGoalSnapshotInput(args.codexGoalJson, repoRoot);
2726
- const aggregate = codexGoalMode(plan) === "aggregate";
2727
- const expected = expectedCodexObjective(plan, goal);
2728
- const reconciliation = reconcileCodexGoalSnapshot(snapshot, { expectedObjective: expected, ...aggregate ? { acceptedObjectives: compatibleCodexObjectives(plan) } : {}, allowedStatuses: ["active"], requireSnapshot: true, requireComplete: false });
2628
+ const reconciliation = reconcileCodexGoalSnapshot(snapshot, {
2629
+ expectedObjective: expectedCodexObjective(plan, goal),
2630
+ acceptedObjectives: compatibleCodexObjectives(plan)
2631
+ });
2729
2632
  if (!reconciliation.ok)
2730
- throw codexSnapshotMismatchError({ reconciliation, snapshot, expectedObjective: expected });
2633
+ throw new CodexGoalSnapshotError(formatCodexGoalReconciliation(reconciliation));
2731
2634
  const now = iso();
2732
2635
  for (const field of BLOCKER_FIELDS)
2733
2636
  Reflect.deleteProperty(goal, field);
@@ -2739,7 +2642,7 @@ async function recordFinalReviewBlockers(repoRoot, args, scope) {
2739
2642
  delete plan.activeGoalId;
2740
2643
  const newGoal = appendBlockerGoal(plan, args, now);
2741
2644
  plan.updatedAt = now;
2742
- const codexGoal = reconciliation.snapshot.raw;
2645
+ const codexGoal = snapshot?.raw;
2743
2646
  const blockedEntry = { at: now, kind: "goal_review_blocked", goalId: goal.id, status: goal.status, evidence: args.evidence, codexGoal };
2744
2647
  const addedEntry = { at: now, kind: "goal_added", goalId: newGoal.id, status: newGoal.status, evidence: args.evidence, message: newGoal.title };
2745
2648
  const summaryEntry = { at: now, kind: "goal_review_blocked", goalId: goal.id, status: goal.status, evidence: args.evidence, codexGoal, message: `Review blockers recorded; appended ${newGoal.id}.` };
@@ -2748,7 +2651,14 @@ async function recordFinalReviewBlockers(repoRoot, args, scope) {
2748
2651
  await writePlan(repoRoot, plan, scope);
2749
2652
  for (const entry of ledgerEntries)
2750
2653
  await appendLedger(repoRoot, entry, scope);
2751
- return { plan, blockedGoal: goal, newGoal, ledgerEntries };
2654
+ return {
2655
+ plan,
2656
+ blockedGoal: goal,
2657
+ newGoal,
2658
+ ledgerEntries,
2659
+ nextActions: reconciliation.warnings,
2660
+ warnings: reconciliation.warnings.filter((warning) => warning.startsWith("driver_objective_differs"))
2661
+ };
2752
2662
  });
2753
2663
  }
2754
2664
 
@@ -3213,7 +3123,7 @@ function ledgerEntry2(proposal, audit, at) {
3213
3123
  // components/ulw-loop/src/cli-subcommands.ts
3214
3124
  async function createGoals(repoRoot, argv, json, scope) {
3215
3125
  const briefFile = readValue(argv, "--brief-file");
3216
- const brief = readValue(argv, "--brief") ?? (briefFile === undefined ? undefined : await readFile6(briefFile, "utf8")) ?? (hasFlag(argv, "--from-stdin") ? await readStdin() : undefined) ?? positionalText(argv);
3126
+ const brief = readValue(argv, "--brief") ?? (briefFile === undefined ? undefined : await readFile5(briefFile, "utf8")) ?? (hasFlag(argv, "--from-stdin") ? await readStdin() : undefined) ?? positionalText(argv);
3217
3127
  if (!brief.trim()) {
3218
3128
  throw new UlwLoopError("Missing brief text. Pass --brief, --brief-file, --from-stdin, or positional text.", "ULW_LOOP_BRIEF_REQUIRED");
3219
3129
  }
@@ -3342,6 +3252,8 @@ async function reviewBlockers(repoRoot, argv, json, scope) {
3342
3252
  blockedGoal: result.blockedGoal,
3343
3253
  goal: result.newGoal,
3344
3254
  ledgerEntries: result.ledgerEntries,
3255
+ nextActions: result.nextActions,
3256
+ warnings: result.warnings,
3345
3257
  summary: summarizeUlwLoopPlan(result.plan)
3346
3258
  });
3347
3259
  } else {
@@ -3479,7 +3391,7 @@ function commandScope(repoRoot, argv) {
3479
3391
  import { readFileSync as readFileSync4 } from "node:fs";
3480
3392
 
3481
3393
  // components/ulw-loop/src/ultrawork-skill-pointer.ts
3482
- import { existsSync as existsSync6, readFileSync as readFileSync3 } from "node:fs";
3394
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
3483
3395
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3484
3396
  var ULTRAWORK_SKILL_POINTER_TEMPLATE = `<ultrawork-mode>
3485
3397
  ULTRAWORK MODE IS ACTIVE FOR THIS TASK.
@@ -3520,7 +3432,7 @@ function buildUltraworkSkillPointer(skillFilePath) {
3520
3432
  }
3521
3433
  function buildUltraworkAdditionalContext(options = {}) {
3522
3434
  const skillFilePath = options.skillFilePath === undefined ? resolveUltraworkSkillFilePath() : options.skillFilePath;
3523
- if (skillFilePath !== null && existsSync6(skillFilePath)) {
3435
+ if (skillFilePath !== null && existsSync5(skillFilePath)) {
3524
3436
  return buildUltraworkSkillPointer(skillFilePath);
3525
3437
  }
3526
3438
  return ULTRAWORK_DIRECTIVE;
@@ -3768,7 +3680,7 @@ function readAll(stdin) {
3768
3680
 
3769
3681
  // components/ulw-loop/src/spawn-guard.ts
3770
3682
  import { randomBytes } from "node:crypto";
3771
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, renameSync, statSync as statSync3, writeFileSync } from "node:fs";
3683
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync5, renameSync, statSync as statSync3, writeFileSync } from "node:fs";
3772
3684
  import { dirname as dirname3, join as join3 } from "node:path";
3773
3685
  var SPAWN_TOOL_TOKENS = new Set(["spawn_agent", "collaborationspawn_agent", "collaboration.spawn_agent"]);
3774
3686
  var DEFAULT_FANOUT_LIMIT = 24;
@@ -4002,7 +3914,7 @@ function reviewSpawnLimit() {
4002
3914
  }
4003
3915
  function isNonEmptyFile(path) {
4004
3916
  try {
4005
- return existsSync7(path) && statSync3(path).size > 0;
3917
+ return existsSync6(path) && statSync3(path).size > 0;
4006
3918
  } catch (error) {
4007
3919
  if (error instanceof Error)
4008
3920
  return false;
@@ -4047,7 +3959,7 @@ function readPlan(goalsPath) {
4047
3959
  }
4048
3960
 
4049
3961
  // components/ulw-loop/src/stop-resume-hook.ts
4050
- import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
3962
+ import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
4051
3963
  import { isAbsolute as isAbsolute2, join as join4, resolve as resolve4, sep as sep2 } from "node:path";
4052
3964
  var RESUME_CAP = 2;
4053
3965
  var CONTEXT_PRESSURE_MARKERS2 = [
@@ -4167,7 +4079,7 @@ function countLedgerLines(ledgerPath) {
4167
4079
  }
4168
4080
  function readCounter(counterPath) {
4169
4081
  try {
4170
- if (!existsSync8(counterPath))
4082
+ if (!existsSync7(counterPath))
4171
4083
  return null;
4172
4084
  const parsed = JSON.parse(readFileSync6(counterPath, "utf8"));
4173
4085
  if (typeof parsed["count"] !== "number" || typeof parsed["ledgerLineCount"] !== "number")
@@ -1,4 +1,4 @@
1
- export type CodexGoalSnapshotStatus = "active" | "complete" | "cancelled" | "failed" | "unknown";
1
+ export type CodexGoalSnapshotStatus = "active" | "complete" | "paused" | "usage_limited" | "budget_limited" | "cancelled" | "failed" | "unknown";
2
2
  export interface CodexGoalSnapshot {
3
3
  available: boolean;
4
4
  objective?: string;
@@ -13,10 +13,7 @@ export interface CodexGoalReconciliation {
13
13
  }
14
14
  export interface ReconcileCodexGoalOptions {
15
15
  expectedObjective: string;
16
- acceptedObjectives?: readonly string[];
17
- allowedStatuses?: readonly CodexGoalSnapshotStatus[];
18
- requireSnapshot?: boolean;
19
- requireComplete?: boolean;
16
+ readonly acceptedObjectives?: readonly string[];
20
17
  }
21
18
  export declare class CodexGoalSnapshotError extends Error {
22
19
  }
@@ -24,16 +21,3 @@ export declare function parseCodexGoalSnapshot(value: unknown): CodexGoalSnapsho
24
21
  export declare function readCodexGoalSnapshotInput(raw: string | undefined, cwd?: string): Promise<CodexGoalSnapshot | null>;
25
22
  export declare function reconcileCodexGoalSnapshot(snapshot: CodexGoalSnapshot | null | undefined, options: ReconcileCodexGoalOptions): CodexGoalReconciliation;
26
23
  export declare function formatCodexGoalReconciliation(reconciliation: CodexGoalReconciliation): string;
27
- export interface CodexGoalMismatchRecovery {
28
- readonly message: string;
29
- readonly details: {
30
- readonly expectedObjective: string;
31
- readonly receivedObjective: string;
32
- };
33
- }
34
- /**
35
- * The reconciliation errors normalize whitespace for comparison, which makes the
36
- * quoted objective unusable as a copy source. Recovery therefore carries the
37
- * plan's `codexObjective` verbatim so the agent can paste it into `update_goal`.
38
- */
39
- export declare function codexGoalMismatchRecovery(expectedObjective: string, snapshot: CodexGoalSnapshot | null | undefined): CodexGoalMismatchRecovery;