lazycodex-ai 5.0.0-beta.62 → 5.0.0-beta.64

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 (215) hide show
  1. package/dist/cli/doctor/checks/browser-provider.d.ts +2 -0
  2. package/dist/cli/index.js +70 -41
  3. package/dist/cli-node/index.js +70 -41
  4. package/package.json +2 -6
  5. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  6. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -2
  7. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  8. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  9. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  10. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  12. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  14. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  16. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  17. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  19. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  21. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  22. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  23. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -2
  25. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +5 -4
  26. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  29. package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +1 -1
  30. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +13 -0
  33. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +5 -4
  34. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +7 -5
  35. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +7 -2
  36. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +7 -4
  37. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +931 -442
  38. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +6 -0
  39. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +20 -12
  40. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +8 -0
  41. package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.d.ts +4 -0
  42. package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.js +18 -0
  43. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.d.ts +1 -0
  44. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.js +37 -0
  45. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.d.ts +1 -0
  46. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.js +24 -12
  47. package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.d.ts +4 -0
  48. package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.js +34 -0
  49. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +1 -0
  50. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +6 -0
  51. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.d.ts +19 -0
  52. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.js +107 -0
  53. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +6 -2
  54. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +54 -25
  55. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.d.ts +10 -3
  56. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.js +38 -31
  57. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +9 -7
  58. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +105 -114
  59. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.d.ts +15 -0
  60. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.js +86 -0
  61. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.d.ts +5 -1
  62. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.js +15 -3
  63. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +11 -8
  64. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/factory.js +28 -12
  65. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.d.ts +335 -0
  66. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.js +109 -10
  67. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/types.d.ts +3 -0
  68. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-budget-io.d.ts +5 -0
  69. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-budget-io.js +67 -0
  70. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +8 -62
  71. package/packages/omo-codex/plugin/components/ulw-loop/dist/state-lock.d.ts +11 -1
  72. package/packages/omo-codex/plugin/components/ulw-loop/dist/state-lock.js +120 -88
  73. package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.d.ts +3 -2
  74. package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.js +18 -10
  75. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +4 -4
  76. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +3 -2
  77. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +4 -4
  78. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +9 -1
  79. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.d.ts +3 -2
  80. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +11 -11
  81. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +8 -17
  82. package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.d.ts +9 -0
  83. package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.js +41 -0
  84. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  85. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  86. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
  87. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +15 -3
  88. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +7 -4
  89. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +26 -14
  90. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +8 -0
  91. package/packages/omo-codex/plugin/components/ulw-loop/src/driver-objective-ack.ts +20 -0
  92. package/packages/omo-codex/plugin/components/ulw-loop/src/evidence-artifacts.ts +45 -0
  93. package/packages/omo-codex/plugin/components/ulw-loop/src/evidence.ts +23 -16
  94. package/packages/omo-codex/plugin/components/ulw-loop/src/ledger.ts +33 -0
  95. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +7 -0
  96. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-commit.ts +124 -0
  97. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +67 -54
  98. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-goal-factory.ts +63 -31
  99. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +126 -140
  100. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-log.ts +84 -0
  101. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-missing-recovery.ts +26 -3
  102. package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +11 -7
  103. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/factory.ts +47 -12
  104. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/manifest.ts +124 -10
  105. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/types.ts +3 -0
  106. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-budget-io.ts +59 -0
  107. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +8 -59
  108. package/packages/omo-codex/plugin/components/ulw-loop/src/state-lock.ts +129 -111
  109. package/packages/omo-codex/plugin/components/ulw-loop/src/status-next-actions.ts +19 -10
  110. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +4 -4
  111. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +5 -4
  112. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +13 -1
  113. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +12 -10
  114. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +8 -16
  115. package/packages/omo-codex/plugin/components/ulw-loop/src/success-criteria-input.ts +56 -0
  116. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-snapshot.test.ts +37 -1
  117. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/barrier.ts +13 -0
  118. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-child.ts +65 -0
  119. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-cli-qa.mjs +76 -0
  120. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-crash.ts +23 -0
  121. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +58 -0
  122. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-writer.ts +65 -0
  123. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/lease-clock.ts +23 -0
  124. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-commit-log.test.ts +252 -0
  125. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-commit-recovery.test.ts +190 -0
  126. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +50 -35
  127. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-log-newest.test.ts +72 -0
  128. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-completed-plan.test.ts +43 -0
  129. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-driver-states.test.ts +33 -4
  130. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-dx.test.ts +219 -0
  131. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-session-context.test.ts +21 -0
  132. package/packages/omo-codex/plugin/components/ulw-loop/test/state-lock.test.ts +92 -0
  133. package/packages/omo-codex/plugin/components/ulw-loop/test/status-next-actions-surface.test.ts +133 -0
  134. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +12 -12
  135. package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +4 -1
  136. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  137. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  138. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  139. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  140. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  141. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  142. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  143. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  144. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  145. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  146. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  147. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  148. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  149. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  150. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  151. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  152. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  153. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  154. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  155. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  156. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  157. package/packages/omo-codex/plugin/package-lock.json +12 -12
  158. package/packages/omo-codex/plugin/package.json +1 -1
  159. package/packages/omo-codex/plugin/scripts/sync-skills.d.mts +4 -0
  160. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +15 -7
  161. package/packages/omo-codex/plugin/skills/ast-grep/SKILL.md +12 -0
  162. package/packages/omo-codex/plugin/skills/debugging/references/tools/playwright-cli.md +79 -161
  163. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
  164. package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +1 -1
  165. package/packages/omo-codex/plugin/skills/frontend/references/design/clone-from-url.md +1 -1
  166. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
  167. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
  168. package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
  169. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +17 -29
  170. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -1
  171. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/package.json +2 -2
  172. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
  173. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
  174. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
  175. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
  176. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
  177. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +9 -3
  178. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
  179. package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
  180. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +5 -4
  181. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +1 -1
  182. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
  183. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +3 -3
  184. package/packages/omo-codex/plugin/skills/visual-qa/AGENTS.md +1 -1
  185. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +1 -1
  186. package/packages/omo-codex/plugin/skills/visual-qa/references/browser-setup.md +75 -0
  187. package/packages/omo-codex/scripts/install-dist/install-local.mjs +6 -4
  188. package/packages/prompts-core/prompts/ultrawork/codex.md +5 -4
  189. package/packages/shared-skills/skills/ast-grep/SKILL.md +12 -0
  190. package/packages/shared-skills/skills/debugging/references/tools/playwright-cli.md +79 -161
  191. package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
  192. package/packages/shared-skills/skills/frontend/references/design/aside.md +1 -1
  193. package/packages/shared-skills/skills/frontend/references/design/clone-from-url.md +1 -1
  194. package/packages/shared-skills/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
  195. package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
  196. package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
  197. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +17 -29
  198. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -1
  199. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/package.json +2 -2
  200. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
  201. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
  202. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
  203. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
  204. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
  205. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +9 -3
  206. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
  207. package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
  208. package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
  209. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
  210. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  211. package/packages/shared-skills/skills/visual-qa/AGENTS.md +1 -1
  212. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
  213. package/packages/shared-skills/skills/visual-qa/references/browser-setup.md +75 -0
  214. package/packages/omo-codex/plugin/skills/visual-qa/references/agent-browser-setup.md +0 -45
  215. package/packages/shared-skills/skills/visual-qa/references/agent-browser-setup.md +0 -45
@@ -1,9 +1,10 @@
1
1
  import { checkpointUlwLoop } from "../checkpoint.js";
2
2
  import { checkpointTemplate } from "../checkpoint-template.js";
3
3
  import { recordEvidence } from "../evidence.js";
4
- import { ulwLoopAttemptEvidenceDir } from "../paths.js";
4
+ import { normalizeUlwLoopSessionId, ulwLoopAttemptEvidenceDir, ulwLoopEvidenceRoot, ulwLoopGoalsRelativePath, } from "../paths.js";
5
5
  import { addUlwLoopGoal, createUlwLoopPlan, startNextUlwLoop, summarizeUlwLoopPlan } from "../plan-crud.js";
6
- import { readUlwLoopPlan } from "../plan-io.js";
6
+ import { listUlwLoopSessionIds, readUlwLoopPlan } from "../plan-io.js";
7
+ import { planMissingError } from "../plan-missing-recovery.js";
7
8
  import { recordFinalReviewBlockers } from "../review-blockers.js";
8
9
  import { UlwLoopError } from "../runtime.js";
9
10
  import { statusNextActions } from "../status-next-actions.js";
@@ -24,8 +25,12 @@ function validateCodexGoalJson(raw) {
24
25
  function validateContext(context) {
25
26
  if (!context.cwd.trim())
26
27
  throw new UlwLoopError("cwd is required.", "ULW_LOOP_CWD_REQUIRED");
27
- if (!context.sessionId.trim())
28
+ const sessionId = context.sessionId.trim();
29
+ if (!sessionId)
28
30
  throw new UlwLoopError("ULW_LOOP_SESSION_ID_REQUIRED: sessionId is required.", "ULW_LOOP_SESSION_ID_REQUIRED");
31
+ const normalizedSessionId = normalizeUlwLoopSessionId(sessionId);
32
+ if (normalizedSessionId === null || /(?:^|[\\/])\.\.(?:[\\/]|$)/.test(sessionId))
33
+ throw new UlwLoopError("ULW_LOOP_SESSION_ID_INVALID: sessionId normalizes to null.", "ULW_LOOP_SESSION_ID_INVALID");
29
34
  if (context.surface !== "omo-senpi" && context.surface !== "lazycodex")
30
35
  throw new UlwLoopError("surface must be omo-senpi or lazycodex.", "ULW_LOOP_SURFACE_INVALID");
31
36
  }
@@ -49,10 +54,11 @@ function isKnownRequest(request) {
49
54
  function unreachable(value) {
50
55
  throw new UlwLoopError(`Unhandled operation: ${String(value)}`, "ULW_LOOP_OPERATION_UNHANDLED");
51
56
  }
52
- function nextActionsFrom(result) {
53
- if (!("nextActions" in result) || !Array.isArray(result.nextActions))
57
+ function stringsFrom(result, key) {
58
+ if (!(key in result))
54
59
  return [];
55
- return result.nextActions.filter((action) => typeof action === "string").slice(0, 8);
60
+ const value = Object.entries(result).find(([name]) => name === key)?.[1];
61
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string").slice(0, 8) : [];
56
62
  }
57
63
  function checkpointWithValidatedSnapshot(context, scope, args) {
58
64
  validateCodexGoalJson(args.codexGoalJson);
@@ -69,10 +75,19 @@ export function createAgentToolkit(context, deps = {}) {
69
75
  const invoke = async (operation, fn) => {
70
76
  try {
71
77
  const result = await fn();
72
- const nextActions = typeof result === "object" && result !== null ? nextActionsFrom(result) : [];
73
- return await notify(operation, { ok: true, operation, result, nextActions });
78
+ const nextActions = typeof result === "object" && result !== null ? stringsFrom(result, "nextActions") : [];
79
+ const warnings = typeof result === "object" && result !== null ? stringsFrom(result, "warnings") : [];
80
+ return await notify(operation, {
81
+ ok: true,
82
+ operation,
83
+ result,
84
+ nextActions,
85
+ ...(warnings.length === 0 ? {} : { warnings }),
86
+ });
74
87
  }
75
88
  catch (error) {
89
+ if (error instanceof UlwLoopError && error.code === "ULW_LOOP_PLAN_MISSING" && context.surface === "omo-senpi")
90
+ return notify(operation, failure(operation, planMissingError(ulwLoopGoalsRelativePath(scope), listUlwLoopSessionIds(context.cwd), context.surface)));
76
91
  const response = caught(operation, error instanceof Error ? error : new Error("ULW_LOOP_ERROR"));
77
92
  return notify(operation, response);
78
93
  }
@@ -107,14 +122,15 @@ export function createAgentToolkit(context, deps = {}) {
107
122
  }
108
123
  },
109
124
  help: () => invoke("help", async () => ULW_LOOP_MANIFEST),
110
- createGoals: (args) => invoke("create-goals", () => createUlwLoopPlan(context.cwd, args, scope)),
125
+ createGoals: (args) => invoke("create-goals", () => createUlwLoopPlan(context.cwd, args, scope, context.surface)),
111
126
  status: () => invoke("status", async () => {
112
127
  const plan = await readUlwLoopPlan(context.cwd, scope);
113
128
  const active = plan.goals.find((goal) => goal.id === plan.activeGoalId);
114
129
  return {
115
130
  plan,
116
131
  summary: summarizeUlwLoopPlan(plan),
117
- nextActions: statusNextActions(plan),
132
+ nextActions: statusNextActions(plan, context.surface),
133
+ evidenceRoot: ulwLoopEvidenceRoot(scope),
118
134
  // Attempt directories are an evidence-layout v2 concept; a v1 plan must not advertise one.
119
135
  ...(active === undefined || plan.evidenceLayoutVersion !== 2
120
136
  ? {}
@@ -125,8 +141,8 @@ export function createAgentToolkit(context, deps = {}) {
125
141
  checkpoint: (args) => invoke("checkpoint", () => args.printTemplate === true
126
142
  ? checkpointTemplate(context.cwd, scope, args.goalId, { surface: context.surface })
127
143
  : checkpointWithValidatedSnapshot(context, scope, args)),
128
- steer: (args) => invoke("steer", () => steerUlwLoop(context.cwd, args, scope)),
129
- addGoal: (args) => invoke("add-goal", () => addUlwLoopGoal(context.cwd, args, scope)),
144
+ steer: (args) => invoke("steer", () => steerUlwLoop(context.cwd, args, scope, context.surface)),
145
+ addGoal: (args) => invoke("add-goal", () => addUlwLoopGoal(context.cwd, args, scope, context.surface)),
130
146
  criteria: (args) => invoke("criteria", async () => {
131
147
  const plan = await readUlwLoopPlan(context.cwd, scope);
132
148
  const goal = plan.goals.find((candidate) => candidate.id === args.goalId);
@@ -2,7 +2,12 @@ export declare const ULW_LOOP_OPERATIONS: readonly ["help", "create-goals", "sta
2
2
  export type UlwLoopOperation = (typeof ULW_LOOP_OPERATIONS)[number];
3
3
  export interface ToolkitOperationManifest {
4
4
  readonly name: UlwLoopOperation;
5
+ /** The camelCase method that runs this operation on every SDK surface. */
6
+ readonly method: string;
5
7
  readonly mutating: boolean;
8
+ readonly description: string;
9
+ /** Argument field -> type notation; a trailing `?` marks an optional field. */
10
+ readonly args: Readonly<Record<string, string>>;
6
11
  }
7
12
  export interface ToolkitManifest {
8
13
  readonly version: 1;
@@ -14,33 +19,363 @@ export declare const ULW_LOOP_MANIFEST: {
14
19
  name: "ulw-loop";
15
20
  operations: ({
16
21
  name: "help";
22
+ method: string;
17
23
  mutating: false;
24
+ description: string;
25
+ args: {
26
+ brief?: never;
27
+ codexGoalMode?: never;
28
+ force?: never;
29
+ validationBatchesJson?: never;
30
+ retryFailed?: never;
31
+ qualityGateJson?: never;
32
+ printTemplate?: never;
33
+ kind?: never;
34
+ source?: never;
35
+ rationale?: never;
36
+ scenario?: never;
37
+ expectedEvidence?: never;
38
+ userModel?: never;
39
+ childGoals?: never;
40
+ revisedTitle?: never;
41
+ revisedObjective?: never;
42
+ pendingOrder?: never;
43
+ blockedReason?: never;
44
+ idempotencyKey?: never;
45
+ successCriteria?: never;
46
+ criterionId?: never;
47
+ status?: never;
48
+ notes?: never;
49
+ artifacts?: never;
50
+ goalId?: never;
51
+ title?: never;
52
+ objective?: never;
53
+ evidence?: never;
54
+ codexGoalJson?: never;
55
+ };
18
56
  } | {
19
57
  name: "create-goals";
58
+ method: string;
20
59
  mutating: true;
60
+ description: string;
61
+ args: {
62
+ brief: string;
63
+ codexGoalMode: string;
64
+ force: string;
65
+ validationBatchesJson: string;
66
+ retryFailed?: never;
67
+ qualityGateJson?: never;
68
+ printTemplate?: never;
69
+ kind?: never;
70
+ source?: never;
71
+ rationale?: never;
72
+ scenario?: never;
73
+ expectedEvidence?: never;
74
+ userModel?: never;
75
+ childGoals?: never;
76
+ revisedTitle?: never;
77
+ revisedObjective?: never;
78
+ pendingOrder?: never;
79
+ blockedReason?: never;
80
+ idempotencyKey?: never;
81
+ successCriteria?: never;
82
+ criterionId?: never;
83
+ status?: never;
84
+ notes?: never;
85
+ artifacts?: never;
86
+ goalId?: never;
87
+ title?: never;
88
+ objective?: never;
89
+ evidence?: never;
90
+ codexGoalJson?: never;
91
+ };
21
92
  } | {
22
93
  name: "status";
94
+ method: string;
23
95
  mutating: false;
96
+ description: string;
97
+ args: {
98
+ brief?: never;
99
+ codexGoalMode?: never;
100
+ force?: never;
101
+ validationBatchesJson?: never;
102
+ retryFailed?: never;
103
+ qualityGateJson?: never;
104
+ printTemplate?: never;
105
+ kind?: never;
106
+ source?: never;
107
+ rationale?: never;
108
+ scenario?: never;
109
+ expectedEvidence?: never;
110
+ userModel?: never;
111
+ childGoals?: never;
112
+ revisedTitle?: never;
113
+ revisedObjective?: never;
114
+ pendingOrder?: never;
115
+ blockedReason?: never;
116
+ idempotencyKey?: never;
117
+ successCriteria?: never;
118
+ criterionId?: never;
119
+ status?: never;
120
+ notes?: never;
121
+ artifacts?: never;
122
+ goalId?: never;
123
+ title?: never;
124
+ objective?: never;
125
+ evidence?: never;
126
+ codexGoalJson?: never;
127
+ };
24
128
  } | {
25
129
  name: "complete-goals";
130
+ method: string;
26
131
  mutating: true;
132
+ description: string;
133
+ args: {
134
+ brief?: never;
135
+ codexGoalMode?: never;
136
+ force?: never;
137
+ validationBatchesJson?: never;
138
+ retryFailed: string;
139
+ qualityGateJson?: never;
140
+ printTemplate?: never;
141
+ kind?: never;
142
+ source?: never;
143
+ rationale?: never;
144
+ scenario?: never;
145
+ expectedEvidence?: never;
146
+ userModel?: never;
147
+ childGoals?: never;
148
+ revisedTitle?: never;
149
+ revisedObjective?: never;
150
+ pendingOrder?: never;
151
+ blockedReason?: never;
152
+ idempotencyKey?: never;
153
+ successCriteria?: never;
154
+ criterionId?: never;
155
+ status?: never;
156
+ notes?: never;
157
+ artifacts?: never;
158
+ goalId?: never;
159
+ title?: never;
160
+ objective?: never;
161
+ evidence?: never;
162
+ codexGoalJson?: never;
163
+ };
27
164
  } | {
28
165
  name: "checkpoint";
166
+ method: string;
29
167
  mutating: true;
168
+ description: string;
169
+ args: {
170
+ brief?: never;
171
+ codexGoalMode?: never;
172
+ force?: never;
173
+ validationBatchesJson?: never;
174
+ retryFailed?: never;
175
+ goalId: string;
176
+ status: string;
177
+ evidence: string;
178
+ codexGoalJson: string;
179
+ qualityGateJson: string;
180
+ printTemplate: string;
181
+ kind?: never;
182
+ source?: never;
183
+ rationale?: never;
184
+ scenario?: never;
185
+ expectedEvidence?: never;
186
+ userModel?: never;
187
+ childGoals?: never;
188
+ revisedTitle?: never;
189
+ revisedObjective?: never;
190
+ pendingOrder?: never;
191
+ blockedReason?: never;
192
+ idempotencyKey?: never;
193
+ successCriteria?: never;
194
+ criterionId?: never;
195
+ notes?: never;
196
+ artifacts?: never;
197
+ title?: never;
198
+ objective?: never;
199
+ };
30
200
  } | {
31
201
  name: "steer";
202
+ method: string;
32
203
  mutating: true;
204
+ description: string;
205
+ args: {
206
+ brief?: never;
207
+ codexGoalMode?: never;
208
+ force?: never;
209
+ validationBatchesJson?: never;
210
+ retryFailed?: never;
211
+ qualityGateJson?: never;
212
+ printTemplate?: never;
213
+ kind: string;
214
+ source: string;
215
+ evidence: string;
216
+ rationale: string;
217
+ goalId: string;
218
+ criterionId: string;
219
+ scenario: string;
220
+ expectedEvidence: string;
221
+ userModel: string;
222
+ title: string;
223
+ objective: string;
224
+ childGoals: string;
225
+ revisedTitle: string;
226
+ revisedObjective: string;
227
+ pendingOrder: string;
228
+ blockedReason: string;
229
+ idempotencyKey: string;
230
+ successCriteria?: never;
231
+ status?: never;
232
+ notes?: never;
233
+ artifacts?: never;
234
+ codexGoalJson?: never;
235
+ };
33
236
  } | {
34
237
  name: "add-goal";
238
+ method: string;
35
239
  mutating: true;
240
+ description: string;
241
+ args: {
242
+ brief?: never;
243
+ codexGoalMode?: never;
244
+ force?: never;
245
+ validationBatchesJson?: never;
246
+ retryFailed?: never;
247
+ qualityGateJson?: never;
248
+ printTemplate?: never;
249
+ kind?: never;
250
+ source?: never;
251
+ rationale?: never;
252
+ scenario?: never;
253
+ expectedEvidence?: never;
254
+ userModel?: never;
255
+ childGoals?: never;
256
+ revisedTitle?: never;
257
+ revisedObjective?: never;
258
+ pendingOrder?: never;
259
+ blockedReason?: never;
260
+ idempotencyKey?: never;
261
+ title: string;
262
+ objective: string;
263
+ successCriteria: string;
264
+ criterionId?: never;
265
+ status?: never;
266
+ notes?: never;
267
+ artifacts?: never;
268
+ goalId?: never;
269
+ evidence?: never;
270
+ codexGoalJson?: never;
271
+ };
36
272
  } | {
37
273
  name: "criteria";
274
+ method: string;
38
275
  mutating: false;
276
+ description: string;
277
+ args: {
278
+ brief?: never;
279
+ codexGoalMode?: never;
280
+ force?: never;
281
+ validationBatchesJson?: never;
282
+ retryFailed?: never;
283
+ qualityGateJson?: never;
284
+ printTemplate?: never;
285
+ kind?: never;
286
+ source?: never;
287
+ rationale?: never;
288
+ scenario?: never;
289
+ expectedEvidence?: never;
290
+ userModel?: never;
291
+ childGoals?: never;
292
+ revisedTitle?: never;
293
+ revisedObjective?: never;
294
+ pendingOrder?: never;
295
+ blockedReason?: never;
296
+ idempotencyKey?: never;
297
+ successCriteria?: never;
298
+ goalId: string;
299
+ criterionId?: never;
300
+ status?: never;
301
+ notes?: never;
302
+ artifacts?: never;
303
+ title?: never;
304
+ objective?: never;
305
+ evidence?: never;
306
+ codexGoalJson?: never;
307
+ };
39
308
  } | {
40
309
  name: "record-evidence";
310
+ method: string;
41
311
  mutating: true;
312
+ description: string;
313
+ args: {
314
+ brief?: never;
315
+ codexGoalMode?: never;
316
+ force?: never;
317
+ validationBatchesJson?: never;
318
+ retryFailed?: never;
319
+ qualityGateJson?: never;
320
+ printTemplate?: never;
321
+ kind?: never;
322
+ source?: never;
323
+ rationale?: never;
324
+ scenario?: never;
325
+ expectedEvidence?: never;
326
+ userModel?: never;
327
+ childGoals?: never;
328
+ revisedTitle?: never;
329
+ revisedObjective?: never;
330
+ pendingOrder?: never;
331
+ blockedReason?: never;
332
+ idempotencyKey?: never;
333
+ successCriteria?: never;
334
+ goalId: string;
335
+ criterionId: string;
336
+ status: string;
337
+ evidence: string;
338
+ notes: string;
339
+ artifacts: string;
340
+ title?: never;
341
+ objective?: never;
342
+ codexGoalJson?: never;
343
+ };
42
344
  } | {
43
345
  name: "record-review-blockers";
346
+ method: string;
44
347
  mutating: true;
348
+ description: string;
349
+ args: {
350
+ brief?: never;
351
+ codexGoalMode?: never;
352
+ force?: never;
353
+ validationBatchesJson?: never;
354
+ retryFailed?: never;
355
+ qualityGateJson?: never;
356
+ printTemplate?: never;
357
+ kind?: never;
358
+ source?: never;
359
+ rationale?: never;
360
+ scenario?: never;
361
+ expectedEvidence?: never;
362
+ userModel?: never;
363
+ childGoals?: never;
364
+ revisedTitle?: never;
365
+ revisedObjective?: never;
366
+ pendingOrder?: never;
367
+ blockedReason?: never;
368
+ idempotencyKey?: never;
369
+ successCriteria?: never;
370
+ criterionId?: never;
371
+ status?: never;
372
+ notes?: never;
373
+ artifacts?: never;
374
+ goalId: string;
375
+ title: string;
376
+ objective: string;
377
+ evidence: string;
378
+ codexGoalJson: string;
379
+ };
45
380
  })[];
46
381
  };
@@ -10,19 +10,118 @@ export const ULW_LOOP_OPERATIONS = [
10
10
  "record-evidence",
11
11
  "record-review-blockers",
12
12
  ];
13
+ const CRITERION_INPUT = '{ scenario: string, expectedEvidence: string, userModel?: "happy" | "edge" | "regression" | "adversarial", essential?: boolean }';
14
+ const STEER_KINDS = '"add_subgoal" | "split_subgoal" | "reorder_pending" | "revise_pending_wording" | "revise_criterion" | "annotate_ledger" | "mark_blocked_superseded"';
13
15
  export const ULW_LOOP_MANIFEST = {
14
16
  version: 1,
15
17
  name: "ulw-loop",
16
18
  operations: [
17
- { name: "help", mutating: false },
18
- { name: "create-goals", mutating: true },
19
- { name: "status", mutating: false },
20
- { name: "complete-goals", mutating: true },
21
- { name: "checkpoint", mutating: true },
22
- { name: "steer", mutating: true },
23
- { name: "add-goal", mutating: true },
24
- { name: "criteria", mutating: false },
25
- { name: "record-evidence", mutating: true },
26
- { name: "record-review-blockers", mutating: true },
19
+ {
20
+ name: "help",
21
+ method: "help",
22
+ mutating: false,
23
+ description: "Return this manifest: every operation with its method name, argument fields, and what it does.",
24
+ args: {},
25
+ },
26
+ {
27
+ name: "create-goals",
28
+ method: "createGoals",
29
+ mutating: true,
30
+ description: "Create the session plan from a brief; one goal per bullet, each seeded with placeholder criteria until revised.",
31
+ args: {
32
+ brief: "string",
33
+ codexGoalMode: '"aggregate" | "per_story"?',
34
+ force: "boolean?",
35
+ validationBatchesJson: "string?",
36
+ },
37
+ },
38
+ {
39
+ name: "status",
40
+ method: "status",
41
+ mutating: false,
42
+ description: "Read the plan, its summary, structured nextActions, the stable evidenceRoot, and the active goal's currentAttemptDir.",
43
+ args: {},
44
+ },
45
+ {
46
+ name: "complete-goals",
47
+ method: "completeGoals",
48
+ mutating: true,
49
+ description: "Acquire the next eligible goal or resume the in-progress one; returns { done: true } once the aggregate is complete.",
50
+ args: { retryFailed: "boolean?" },
51
+ },
52
+ {
53
+ name: "checkpoint",
54
+ method: "checkpoint",
55
+ mutating: true,
56
+ description: "Close the goal as complete, failed, or blocked with evidence; the final goal also needs the quality gate. printTemplate returns that gate's template instead.",
57
+ args: {
58
+ goalId: "string",
59
+ status: '"complete" | "failed" | "blocked"',
60
+ evidence: "string",
61
+ codexGoalJson: "string?",
62
+ qualityGateJson: "string?",
63
+ printTemplate: "true? (with goalId? only)",
64
+ },
65
+ },
66
+ {
67
+ name: "steer",
68
+ method: "steer",
69
+ mutating: true,
70
+ description: "Propose an evidence-backed plan mutation; revise_criterion replaces a criterion's scenario, expectedEvidence, or userModel.",
71
+ args: {
72
+ kind: STEER_KINDS,
73
+ source: '"finding" | "user_prompt_submit" | "cli"',
74
+ evidence: "string",
75
+ rationale: "string",
76
+ goalId: "string?",
77
+ criterionId: "string? (revise_criterion)",
78
+ scenario: "string? (revise_criterion)",
79
+ expectedEvidence: "string? (revise_criterion)",
80
+ userModel: '"happy" | "edge" | "regression" | "adversarial"? (revise_criterion)',
81
+ title: "string? (add_subgoal)",
82
+ objective: "string? (add_subgoal)",
83
+ childGoals: "{ title: string, objective: string }[]? (split_subgoal)",
84
+ revisedTitle: "string? (revise_pending_wording)",
85
+ revisedObjective: "string? (revise_pending_wording)",
86
+ pendingOrder: "string[]? (reorder_pending)",
87
+ blockedReason: "string? (mark_blocked_superseded)",
88
+ idempotencyKey: "string?",
89
+ },
90
+ },
91
+ {
92
+ name: "add-goal",
93
+ method: "addGoal",
94
+ mutating: true,
95
+ description: "Append a goal; pass successCriteria to define its criteria in the same call, otherwise placeholders name the revise call.",
96
+ args: { title: "string", objective: "string", successCriteria: `${CRITERION_INPUT}[]?` },
97
+ },
98
+ {
99
+ name: "criteria",
100
+ method: "criteria",
101
+ mutating: false,
102
+ description: "List one goal's success criteria with their status and captured evidence.",
103
+ args: { goalId: "string" },
104
+ },
105
+ {
106
+ name: "record-evidence",
107
+ method: "recordEvidence",
108
+ mutating: true,
109
+ description: "Record a criterion's pass, fail, or blocked evidence; artifacts must exist (resolved against the session cwd) and are stored with the criterion and the ledger entry.",
110
+ args: {
111
+ goalId: "string",
112
+ criterionId: "string",
113
+ status: '"pass" | "fail" | "blocked"',
114
+ evidence: "string",
115
+ notes: "string?",
116
+ artifacts: "string[]?",
117
+ },
118
+ },
119
+ {
120
+ name: "record-review-blockers",
121
+ method: "recordReviewBlockers",
122
+ mutating: true,
123
+ description: "Mark the final goal review_blocked and append the blocker goal the reviewer verdict names.",
124
+ args: { goalId: "string", title: "string", objective: "string", evidence: "string", codexGoalJson: "string?" },
125
+ },
27
126
  ],
28
127
  };
@@ -82,11 +82,14 @@ export interface ToolkitFailure<Operation extends string = string> {
82
82
  readonly ok: false;
83
83
  readonly operation: Operation;
84
84
  readonly error: ToolkitError;
85
+ readonly warnings?: readonly string[];
85
86
  }
86
87
  type StatusResult = {
87
88
  readonly plan: UlwLoopPlan;
88
89
  readonly summary: ReturnType<typeof summarizeUlwLoopPlan>;
89
90
  readonly nextActions: readonly string[];
91
+ /** Plan-level evidence directory, stable for the whole run (relative to the session cwd). */
92
+ readonly evidenceRoot: string;
90
93
  readonly currentAttemptDir?: string;
91
94
  };
92
95
  type ResultFor<Operation extends UlwLoopOperation> = Operation extends "help" ? typeof ULW_LOOP_MANIFEST : Operation extends "create-goals" ? Awaited<ReturnType<typeof createUlwLoopPlan>> : Operation extends "status" ? StatusResult : Operation extends "complete-goals" ? Awaited<ReturnType<typeof startNextUlwLoop>> : Operation extends "checkpoint" ? CheckpointUlwLoopResult | CheckpointTemplate : Operation extends "steer" ? Awaited<ReturnType<typeof steerUlwLoop>> : Operation extends "add-goal" ? Awaited<ReturnType<typeof addUlwLoopGoal>> : Operation extends "criteria" ? {
@@ -0,0 +1,5 @@
1
+ export declare function readAdmissionBreaker(sessionId: string): string | null;
2
+ export declare function atomicWriteJson(targetPath: string, data: unknown): void;
3
+ export declare function isNonEmptyFile(path: string): boolean;
4
+ export declare function readCount(counterPath: string): number;
5
+ export declare function readCounts(counterPath: string): Record<string, number>;
@@ -0,0 +1,67 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { existsSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ export function readAdmissionBreaker(sessionId) {
5
+ const dataDir = process.env["PLUGIN_DATA"];
6
+ if (typeof dataDir !== "string")
7
+ return null;
8
+ try {
9
+ const value = JSON.parse(readFileSync(join(dataDir, "spawn-breaker", `${sessionId}.json`), "utf8"));
10
+ return typeof value === "object" && value !== null && "reason" in value && typeof value.reason === "string"
11
+ ? value.reason
12
+ : "capacity limit";
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ // These hook budget files are not plan/audit state and remain outside the log.
19
+ export function atomicWriteJson(targetPath, data) {
20
+ const tmp = join(dirname(targetPath), `.tmp-${randomBytes(6).toString("hex")}`);
21
+ writeFileSync(tmp, JSON.stringify(data));
22
+ renameSync(tmp, targetPath);
23
+ }
24
+ export function isNonEmptyFile(path) {
25
+ try {
26
+ return existsSync(path) && statSync(path).size > 0;
27
+ }
28
+ catch (error) {
29
+ if (error instanceof Error)
30
+ return false;
31
+ throw error;
32
+ }
33
+ }
34
+ export function readCount(counterPath) {
35
+ try {
36
+ const parsed = JSON.parse(readFileSync(counterPath, "utf8"));
37
+ return typeof parsed === "object" &&
38
+ parsed !== null &&
39
+ "count" in parsed &&
40
+ typeof parsed.count === "number" &&
41
+ parsed.count >= 0
42
+ ? parsed.count
43
+ : 0;
44
+ }
45
+ catch (error) {
46
+ if (error instanceof Error)
47
+ return 0;
48
+ throw error;
49
+ }
50
+ }
51
+ export function readCounts(counterPath) {
52
+ try {
53
+ const parsed = JSON.parse(readFileSync(counterPath, "utf8"));
54
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
55
+ return {};
56
+ const counts = {};
57
+ for (const [key, value] of Object.entries(parsed))
58
+ if (typeof value === "number" && value >= 0)
59
+ counts[key] = value;
60
+ return counts;
61
+ }
62
+ catch (error) {
63
+ if (error instanceof Error)
64
+ return {};
65
+ throw error;
66
+ }
67
+ }