oh-my-opencode 5.0.0-beta.30 → 5.0.0-beta.32

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 (278) hide show
  1. package/dist/cli/index.js +178 -37
  2. package/dist/cli-node/index.js +178 -37
  3. package/dist/config/schema/background-task.d.ts +1 -0
  4. package/dist/config/schema/oh-my-opencode-config.d.ts +1 -0
  5. package/dist/features/background-agent/constants.d.ts +0 -7
  6. package/dist/features/background-agent/manager.d.ts +2 -1
  7. package/dist/features/background-agent/parent-wake-dispatched-tracker.d.ts +0 -1
  8. package/dist/features/background-agent/parent-wake-notifier-types.d.ts +1 -1
  9. package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -1
  10. package/dist/features/background-agent/parent-wake-session-inspector.d.ts +1 -1
  11. package/dist/features/background-agent/subagent-spawn-limits.d.ts +8 -0
  12. package/dist/features/monitor/manager-internals.d.ts +1 -1
  13. package/dist/features/monitor/output-injector-types.d.ts +1 -1
  14. package/dist/features/task-toast-manager/manager.d.ts +0 -24
  15. package/dist/features/team-mode/tools/messaging-live-delivery-recipient.d.ts +1 -0
  16. package/dist/features/team-mode/tools/messaging-runtime.d.ts +1 -0
  17. package/dist/hooks/anthropic-context-window-limit-recovery/message-storage-directory.d.ts +0 -3
  18. package/dist/hooks/anthropic-context-window-limit-recovery/state.d.ts +1 -0
  19. package/dist/hooks/anthropic-context-window-limit-recovery/storage/empty-messages.d.ts +0 -1
  20. package/dist/hooks/anthropic-context-window-limit-recovery/tool-result-storage.d.ts +11 -0
  21. package/dist/hooks/claude-code-hooks/session-hook-state.d.ts +1 -0
  22. package/dist/hooks/claude-code-hooks/stop.d.ts +2 -0
  23. package/dist/hooks/claude-code-hooks/todo.d.ts +0 -1
  24. package/dist/hooks/claude-code-hooks/transcript.d.ts +2 -0
  25. package/dist/hooks/fsync-skip-warning/index.d.ts +4 -0
  26. package/dist/hooks/hashline-edit-diff-enhancer/hook.d.ts +1 -0
  27. package/dist/hooks/hashline-edit-diff-enhancer/pending-captures.d.ts +13 -0
  28. package/dist/hooks/keyword-detector/hook.d.ts +7 -0
  29. package/dist/hooks/keyword-detector/ultrawork/source-detector.d.ts +0 -1
  30. package/dist/hooks/model-fallback/hook.d.ts +1 -1
  31. package/dist/index.js +681 -273
  32. package/dist/oh-my-opencode.schema.json +5 -0
  33. package/dist/plugin-dispose.d.ts +3 -0
  34. package/dist/shared/agent-tool-restrictions.d.ts +0 -1
  35. package/dist/skills/ultimate-browsing/references/insane-search/README.md +9 -2
  36. package/dist/tools/delegate-task/sync-session-cleanup.d.ts +3 -0
  37. package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -0
  38. package/dist/tools/delegate-task/types.d.ts +1 -0
  39. package/dist/tools/session-manager/directory-filter.d.ts +2 -0
  40. package/dist/tui.js +148 -9
  41. package/package.json +16 -15
  42. package/packages/git-bash-mcp/dist/cli.js +1 -0
  43. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +47 -19
  44. package/packages/lsp-core/src/lsp/client-diagnostics-pull-timeout.test.ts +64 -0
  45. package/packages/lsp-core/src/lsp/client-wrapper.test.ts +97 -0
  46. package/packages/lsp-core/src/lsp/client-wrapper.ts +21 -6
  47. package/packages/lsp-core/src/lsp/client.ts +6 -3
  48. package/packages/lsp-core/src/lsp/constants.ts +5 -0
  49. package/packages/lsp-core/src/lsp/controlled-clock-test-support.ts +61 -0
  50. package/packages/lsp-core/src/lsp/errors.ts +15 -0
  51. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +10 -3
  52. package/packages/lsp-core/src/lsp/manager-lifecycle.test.ts +262 -0
  53. package/packages/lsp-core/src/lsp/manager.ts +106 -13
  54. package/packages/lsp-core/src/lsp/timer-provider.ts +13 -0
  55. package/packages/lsp-core/src/lsp/transport-request-timeout.test.ts +73 -0
  56. package/packages/lsp-core/src/lsp/transport.ts +7 -2
  57. package/packages/lsp-core/src/lsp/workspace-document-state.test.ts +84 -1
  58. package/packages/lsp-core/src/lsp/workspace-document-state.ts +30 -11
  59. package/packages/lsp-core/src/lsp/workspace-markers.ts +18 -1
  60. package/packages/lsp-core/src/mcp.ts +3 -0
  61. package/packages/lsp-core/src/tools/parameters.test.ts +51 -0
  62. package/packages/lsp-daemon/dist/cli.js +155 -34
  63. package/packages/lsp-daemon/dist/client.js +155 -34
  64. package/packages/lsp-daemon/dist/index.js +155 -34
  65. package/packages/lsp-daemon/dist/proxy.js +3 -0
  66. package/packages/lsp-tools-mcp/dist/cli.js +155 -34
  67. package/packages/lsp-tools-mcp/dist/lsp/manager.js +134 -28
  68. package/packages/lsp-tools-mcp/dist/mcp.js +155 -34
  69. package/packages/lsp-tools-mcp/dist/tools.js +154 -34
  70. package/packages/lsp-tools-mcp/package.json +1 -1
  71. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  72. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  73. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  74. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +17 -2
  75. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +17 -2
  76. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  77. package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +12 -0
  78. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable-idle.test.ts +47 -0
  79. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  80. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  81. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  82. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  83. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  84. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  85. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  86. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +152 -34
  87. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  88. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  89. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  90. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  91. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  92. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  93. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  94. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  95. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +5 -4
  96. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +489 -4
  97. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  98. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -2
  99. package/packages/omo-codex/plugin/components/ultrawork/scripts/sync-directive.mjs +30 -14
  100. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +4 -0
  101. package/packages/omo-codex/plugin/components/ultrawork/src/directive.ts +5 -2
  102. package/packages/omo-codex/plugin/components/ultrawork/test/directive-source.test.ts +14 -8
  103. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +12 -1
  104. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
  105. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  106. package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +5 -2
  107. package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -0
  108. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +9 -0
  109. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.d.ts +14 -0
  110. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.js +10 -1
  111. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.d.ts +8 -0
  112. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.js +113 -0
  113. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +9 -5
  114. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
  115. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
  116. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +1 -0
  117. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +2 -0
  118. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +856 -347
  119. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.d.ts +2 -0
  120. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +27 -6
  121. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +13 -0
  122. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +14 -0
  123. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +17 -9
  124. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +17 -2
  125. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.d.ts +13 -0
  126. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.js +19 -0
  127. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.d.ts +2 -0
  128. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.js +53 -0
  129. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.d.ts +9 -0
  130. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.js +85 -0
  131. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.js +1 -1
  132. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +4 -2
  133. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +41 -102
  134. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +4 -2
  135. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +185 -14
  136. package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.d.ts +6 -0
  137. package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.js +33 -0
  138. package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.d.ts +18 -0
  139. package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.js +65 -0
  140. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  141. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  142. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +17 -0
  143. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +7 -0
  144. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-reconciliation.ts +25 -1
  145. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-template.ts +127 -0
  146. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +10 -12
  147. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +2 -1
  148. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +1 -0
  149. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +2 -0
  150. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +34 -5
  151. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +23 -0
  152. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +19 -9
  153. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +18 -3
  154. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-missing-recovery.ts +27 -0
  155. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-aggregate.ts +57 -0
  156. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-artifacts.ts +103 -0
  157. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-fields.ts +1 -1
  158. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +54 -123
  159. package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +4 -2
  160. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +187 -12
  161. package/packages/omo-codex/plugin/components/ulw-loop/src/status-next-actions.ts +36 -0
  162. package/packages/omo-codex/plugin/components/ulw-loop/src/surface.ts +95 -0
  163. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-final.test.ts +107 -1
  164. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-template.test.ts +92 -0
  165. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-status-next-actions.test.ts +183 -0
  166. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +35 -0
  167. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +7 -3
  168. package/packages/omo-codex/plugin/components/ulw-loop/test/guided-recovery.test.ts +105 -0
  169. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate.test.ts +101 -0
  170. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-roles.test.ts +46 -2
  171. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-senpi-surface.test.ts +131 -0
  172. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +5 -0
  173. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +318 -1
  174. package/packages/omo-codex/plugin/components/ulw-loop/test/surface.test.ts +74 -0
  175. package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +9 -3
  176. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  177. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  178. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  179. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  180. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  181. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  182. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  183. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  184. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  185. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  186. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  187. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  188. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  189. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  190. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  191. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  192. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  193. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  194. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
  195. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  196. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  197. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  198. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  199. package/packages/omo-codex/plugin/package-lock.json +13 -13
  200. package/packages/omo-codex/plugin/package.json +1 -1
  201. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +31 -2
  202. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +9 -2
  203. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +17 -0
  204. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +11 -1
  205. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +13 -0
  206. package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +5 -2
  207. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
  208. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +9 -2
  209. package/dist/features/builtin-skills/index.d.ts +0 -1
  210. package/dist/features/builtin-skills/skill-file-loader.d.ts +0 -1
  211. package/dist/features/builtin-skills/skills/agent-browser-skill.d.ts +0 -1
  212. package/dist/features/builtin-skills/skills/agent-browser-template.d.ts +0 -1
  213. package/dist/features/builtin-skills/skills/debugging.d.ts +0 -1
  214. package/dist/features/builtin-skills/skills/dev-browser.d.ts +0 -1
  215. package/dist/features/builtin-skills/skills/frontend.d.ts +0 -1
  216. package/dist/features/builtin-skills/skills/git-master-sections/commit-atomic-planning.d.ts +0 -1
  217. package/dist/features/builtin-skills/skills/git-master-sections/commit-context-analysis.d.ts +0 -1
  218. package/dist/features/builtin-skills/skills/git-master-sections/commit-execution-verification.d.ts +0 -1
  219. package/dist/features/builtin-skills/skills/git-master-sections/commit-workflow.d.ts +0 -1
  220. package/dist/features/builtin-skills/skills/git-master-sections/history-search-workflow.d.ts +0 -1
  221. package/dist/features/builtin-skills/skills/git-master-sections/overview.d.ts +0 -1
  222. package/dist/features/builtin-skills/skills/git-master-sections/quick-reference.d.ts +0 -1
  223. package/dist/features/builtin-skills/skills/git-master-sections/rebase-workflow.d.ts +0 -1
  224. package/dist/features/builtin-skills/skills/git-master-skill-metadata.d.ts +0 -1
  225. package/dist/features/builtin-skills/skills/git-master.d.ts +0 -1
  226. package/dist/features/builtin-skills/skills/index.d.ts +0 -1
  227. package/dist/features/builtin-skills/skills/init-deep.d.ts +0 -1
  228. package/dist/features/builtin-skills/skills/playwright-cli.d.ts +0 -1
  229. package/dist/features/builtin-skills/skills/playwright-mcp-skill.d.ts +0 -1
  230. package/dist/features/builtin-skills/skills/playwright.d.ts +0 -1
  231. package/dist/features/builtin-skills/skills/remove-ai-slops.d.ts +0 -1
  232. package/dist/features/builtin-skills/skills/review-work.d.ts +0 -1
  233. package/dist/features/builtin-skills/skills/security-research.d.ts +0 -1
  234. package/dist/features/builtin-skills/skills/security-review.d.ts +0 -1
  235. package/dist/features/builtin-skills/skills/team-mode.d.ts +0 -1
  236. package/dist/features/builtin-skills/skills/visual-qa.d.ts +0 -1
  237. package/dist/features/builtin-skills/skills.d.ts +0 -1
  238. package/dist/features/builtin-skills/types.d.ts +0 -1
  239. package/dist/features/claude-code-plugin-loader/agent-loader.d.ts +0 -1
  240. package/dist/features/claude-code-plugin-loader/command-loader.d.ts +0 -1
  241. package/dist/features/claude-code-plugin-loader/discovery-core.d.ts +0 -1
  242. package/dist/features/claude-code-plugin-loader/discovery-paths.d.ts +0 -1
  243. package/dist/features/claude-code-plugin-loader/discovery.d.ts +0 -1
  244. package/dist/features/claude-code-plugin-loader/hook-loader.d.ts +0 -1
  245. package/dist/features/claude-code-plugin-loader/index.d.ts +0 -1
  246. package/dist/features/claude-code-plugin-loader/install-path-resolver.d.ts +0 -1
  247. package/dist/features/claude-code-plugin-loader/installed-plugin-database.d.ts +0 -1
  248. package/dist/features/claude-code-plugin-loader/loaded-plugin.d.ts +0 -1
  249. package/dist/features/claude-code-plugin-loader/loader.d.ts +0 -1
  250. package/dist/features/claude-code-plugin-loader/mcp-server-loader.d.ts +0 -1
  251. package/dist/features/claude-code-plugin-loader/plugin-key.d.ts +0 -1
  252. package/dist/features/claude-code-plugin-loader/plugin-manifest.d.ts +0 -1
  253. package/dist/features/claude-code-plugin-loader/plugin-path-resolver.d.ts +0 -1
  254. package/dist/features/claude-code-plugin-loader/plugin-settings.d.ts +0 -1
  255. package/dist/features/claude-code-plugin-loader/scope-filter.d.ts +0 -1
  256. package/dist/features/claude-code-plugin-loader/skill-loader.d.ts +0 -1
  257. package/dist/features/claude-code-plugin-loader/types.d.ts +0 -1
  258. package/dist/hooks/ralph-loop/completion-promise-detector-test-input.d.ts +0 -11
  259. package/dist/hooks/ralph-loop/loop-session-recovery.d.ts +0 -7
  260. package/dist/shared/prompt-async-gate/message-inspection-error.d.ts +0 -1
  261. package/dist/shared/prompt-async-gate/pending-tool-turn.d.ts +0 -1
  262. package/dist/shared/prompt-async-gate/prompt-message-state.d.ts +0 -1
  263. package/dist/shared/prompt-async-gate/queue.d.ts +0 -1
  264. package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +0 -1
  265. package/dist/shared/prompt-async-gate/reservations.d.ts +0 -1
  266. package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +0 -1
  267. package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +0 -1
  268. package/dist/shared/prompt-async-gate/timing.d.ts +0 -1
  269. package/dist/shared/prompt-async-gate/types.d.ts +0 -1
  270. package/dist/tools/call-omo-agent/message-storage-directory.d.ts +0 -1
  271. package/dist/tools/delegate-task/model-string-parser.d.ts +0 -1
  272. package/packages/lsp-tools-mcp/dist/lsp/cleanup-errors.d.ts +0 -1
  273. package/packages/lsp-tools-mcp/dist/lsp/constants.d.ts +0 -1
  274. package/packages/lsp-tools-mcp/dist/lsp/language-mappings.d.ts +0 -1
  275. package/packages/lsp-tools-mcp/dist/lsp/process-signal-cleanup.d.ts +0 -1
  276. package/packages/lsp-tools-mcp/dist/missing-dependency-result.d.ts +0 -1
  277. package/packages/omo-codex/plugin/components/ultrawork/directive.md +0 -483
  278. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +0 -490
@@ -1,14 +1,23 @@
1
- import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
1
+ import { randomBytes } from "node:crypto";
2
+ import { existsSync, mkdirSync, openSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
3
+ import { dirname, join } from "node:path";
3
4
  import { parsePreToolUsePayload } from "./codex-hook.js";
4
5
  import { isFinalRunCompletionCandidate } from "./goal-status.js";
5
6
  import { ulwLoopAttemptEvidenceDir, ulwLoopDir } from "./paths.js";
7
+ import { GATE_REVIEWER_AGENT_NAMES, REVIEWER_ROLES_BY_SURFACE, resolveToolkitSurface, reviewerRolesFor, } from "./surface.js";
6
8
  // spawn_agent = v1; collaborationspawn_agent = the delimiter-free flattened v2
7
9
  // hook token from codex-rs hook_names.rs; collaboration.spawn_agent = the
8
10
  // dotted token observed live in the task-1 probe (hook-tool-tokens.txt).
9
11
  const SPAWN_TOOL_TOKENS = new Set(["spawn_agent", "collaborationspawn_agent", "collaboration.spawn_agent"]);
10
12
  const DEFAULT_FANOUT_LIMIT = 60;
11
- const GATE_MESSAGE_PATTERN = /lazycodex-gate-reviewer|final gate review/i;
13
+ const DEFAULT_REVIEW_SPAWN_LIMIT = 3;
14
+ const GATE_MESSAGE_PATTERN = /lazycodex-gate-reviewer|omo-senpi-gate-reviewer|final gate review/i;
15
+ const REVIEW_AGENT_TYPES = [
16
+ ...Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.gateReview),
17
+ ...Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.codeReview),
18
+ ...Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.manualQa),
19
+ ];
20
+ const REVIEW_AGENT_TYPE_SET = new Set(REVIEW_AGENT_TYPES);
12
21
  export function applySpawnGuards(payload) {
13
22
  if (payload.hook_event_name !== "PreToolUse" || !SPAWN_TOOL_TOKENS.has(payload.tool_name))
14
23
  return "";
@@ -16,12 +25,18 @@ export function applySpawnGuards(payload) {
16
25
  const plan = readPlan(join(stateDir, "goals.json"));
17
26
  if (plan === null)
18
27
  return "";
19
- const fanOutDenial = consumeFanOutBudget(stateDir);
20
- if (fanOutDenial !== null)
21
- return deny(fanOutDenial);
28
+ const fanOutPeek = peekFanOutBudget(stateDir);
29
+ if (fanOutPeek !== null)
30
+ return deny(fanOutPeek);
22
31
  const missingArtifact = missingGateArtifact(payload, plan);
23
32
  if (missingArtifact !== null)
24
33
  return deny(`spawn code-review + QA first; gate audits their artifacts: missing ${missingArtifact}`);
34
+ const reviewDenial = consumeReviewSpawnBudget(payload, plan, stateDir);
35
+ if (reviewDenial !== null)
36
+ return deny(reviewDenial);
37
+ const fanOutDenial = consumeFanOutBudget(stateDir);
38
+ if (fanOutDenial !== null)
39
+ return deny(fanOutDenial);
25
40
  return "";
26
41
  }
27
42
  export async function runSpawnGuardCli(stdin, stdout) {
@@ -41,17 +56,51 @@ export async function runSpawnGuardCli(stdin, stdout) {
41
56
  return;
42
57
  }
43
58
  }
59
+ // Read-only fan-out eligibility check. Returns a denial reason when the next
60
+ // spawn would exceed the limit, without incrementing the counter. Call this
61
+ // before charging any per-reviewer quota so a saturated global cap cannot
62
+ // silently consume reviewer allowances for spawns that will never run.
63
+ function peekFanOutBudget(stateDir) {
64
+ const counterPath = join(stateDir, "spawn-count.json");
65
+ const count = readCount(counterPath) + 1;
66
+ const limit = fanOutLimit();
67
+ if (count <= limit)
68
+ return null;
69
+ return `ulw-loop spawn fan-out cap reached (${count}/${limit}). Consolidate work into the agents already running, or raise OMO_SPAWN_FANOUT_LIMIT if this volume is intentional.`;
70
+ }
44
71
  // Per-session spawn counter; depth/lineage tracking is descoped — this is a
45
72
  // total-volume backstop against fan-out explosions, not a recursion tracker.
46
73
  function consumeFanOutBudget(stateDir) {
47
74
  const counterPath = join(stateDir, "spawn-count.json");
48
75
  const count = readCount(counterPath) + 1;
49
- writeFileSync(counterPath, JSON.stringify({ count }));
76
+ atomicWriteJson(counterPath, { count });
50
77
  const limit = fanOutLimit();
51
78
  if (count <= limit)
52
79
  return null;
53
80
  return `ulw-loop spawn fan-out cap reached (${count}/${limit}). Consolidate work into the agents already running, or raise OMO_SPAWN_FANOUT_LIMIT if this volume is intentional.`;
54
81
  }
82
+ function consumeReviewSpawnBudget(payload, plan, stateDir) {
83
+ const agentType = reviewAgentType(payload.tool_input);
84
+ if (agentType === null)
85
+ return null;
86
+ const goal = plan.goals.find((candidate) => candidate.id === plan.activeGoalId) ??
87
+ plan.goals.find((candidate) => isFinalRunCompletionCandidate(plan, candidate));
88
+ if (goal === undefined)
89
+ return null;
90
+ const counterPath = join(stateDir, "review-spawn-counts.json");
91
+ const lockPath = `${counterPath}.lock`;
92
+ const limit = reviewSpawnLimit();
93
+ return withExclusiveLock(lockPath, () => {
94
+ const counts = readCounts(counterPath);
95
+ const key = `${agentType}:${goal.id}:a${goal.attempt}`;
96
+ const count = (counts[key] ?? 0) + 1;
97
+ if (count > limit)
98
+ return `ulw-loop reviewer no-progress cap reached (${agentType} ${count}/${limit}) for ${goal.id} attempt ${goal.attempt}. Consolidate existing review findings, or checkpoint and start a new attempt after concrete progress.`;
99
+ counts[key] = count;
100
+ atomicWriteJson(counterPath, counts);
101
+ return null;
102
+ });
103
+ }
55
104
  function missingGateArtifact(payload, plan) {
56
105
  if (!isGateReviewerSpawn(payload.tool_input))
57
106
  return null;
@@ -61,9 +110,11 @@ function missingGateArtifact(payload, plan) {
61
110
  if (!goal.successCriteria.every((criterion) => criterion.status === "pass"))
62
111
  return null;
63
112
  const scope = { sessionId: payload.session_id };
113
+ const surface = resolveToolkitSurface();
114
+ const requiredArtifacts = surface === "omo-senpi" ? [`${goal.id}-manual-qa.md`] : [`${goal.id}-code-review.md`, `${goal.id}-manual-qa.md`];
64
115
  if (plan.evidenceLayoutVersion === 2) {
65
116
  const attemptDir = ulwLoopAttemptEvidenceDir(goal.id, goal.attempt, scope);
66
- for (const name of [`${goal.id}-code-review.md`, `${goal.id}-manual-qa.md`]) {
117
+ for (const name of requiredArtifacts) {
67
118
  const relative = `${attemptDir}/${name}`;
68
119
  if (!isNonEmptyFile(join(payload.cwd, relative)))
69
120
  return relative;
@@ -71,22 +122,117 @@ function missingGateArtifact(payload, plan) {
71
122
  return null;
72
123
  }
73
124
  const flatReport = `.omo/evidence/${goal.id}-code-review.md`;
74
- if (!isNonEmptyFile(join(payload.cwd, flatReport)))
125
+ if (surface !== "omo-senpi" && !isNonEmptyFile(join(payload.cwd, flatReport)))
75
126
  return flatReport;
127
+ if (surface === "omo-senpi") {
128
+ const manualQa = `.omo/evidence/${goal.id}-manual-qa.md`;
129
+ return isNonEmptyFile(join(payload.cwd, manualQa)) ? null : manualQa;
130
+ }
76
131
  // v1 manual-QA approximation: any other non-empty evidence file counts.
77
132
  if (!hasOtherEvidenceFile(join(payload.cwd, ".omo", "evidence"), `${goal.id}-code-review.md`))
78
- return `.omo/evidence/<any manual-QA artifact besides ${goal.id}-code-review.md>`;
133
+ return `.omo/evidence/${goal.id}-manual-qa.md`;
79
134
  return null;
80
135
  }
81
136
  function isGateReviewerSpawn(toolInput) {
137
+ const agentType = reviewAgentType(toolInput);
138
+ return agentType !== null && GATE_REVIEWER_AGENT_NAMES.has(agentType);
139
+ }
140
+ function reviewAgentType(toolInput) {
82
141
  if (typeof toolInput !== "object" || toolInput === null)
83
- return false;
142
+ return null;
84
143
  const record = toolInput;
85
144
  const agentType = record["agent_type"];
86
- if (typeof agentType === "string")
87
- return agentType === "lazycodex-gate-reviewer";
145
+ if (typeof agentType === "string") {
146
+ // V1: agent_type is present — only reviewer types proceed; any other type is not a review spawn.
147
+ if (!REVIEW_AGENT_TYPE_SET.has(agentType))
148
+ return null;
149
+ return activeSurfaceReviewerAlias(agentType);
150
+ }
88
151
  const message = record["message"];
89
- return typeof message === "string" && GATE_MESSAGE_PATTERN.test(message);
152
+ if (typeof message !== "string")
153
+ return null;
154
+ const normalizedMessage = message.toLowerCase();
155
+ // Explicit "act as <role>" assignment takes priority. If the assigned role is not a reviewer,
156
+ // treat the spawn as non-review so a message that merely mentions a reviewer name does not
157
+ // accidentally charge that reviewer's quota.
158
+ const allRoleNames = [...REVIEW_AGENT_TYPES];
159
+ const explicitAssignment = allRoleNames
160
+ .map((name) => ({
161
+ name,
162
+ index: normalizedMessage.search(new RegExp(`\\bact as (?:an? )?${name}\\b`)),
163
+ }))
164
+ .filter(({ index }) => index >= 0)
165
+ .sort((left, right) => left.index - right.index)[0];
166
+ if (explicitAssignment !== undefined)
167
+ return activeSurfaceReviewerAlias(explicitAssignment.name);
168
+ // Check for an explicit "act as <non-reviewer-role>" assignment. If found, the spawn is not a
169
+ // review spawn even if the message body mentions a reviewer name.
170
+ const nonReviewerActAs = /\bact as (?:an? )?\S+/.test(normalizedMessage);
171
+ if (nonReviewerActAs)
172
+ return null;
173
+ const namedReviewer = REVIEW_AGENT_TYPES.find((name) => normalizedMessage.includes(name));
174
+ if (namedReviewer !== undefined)
175
+ return activeSurfaceReviewerAlias(namedReviewer);
176
+ return GATE_MESSAGE_PATTERN.test(message) ? reviewerRolesFor(resolveToolkitSurface()).gateReview : null;
177
+ }
178
+ function activeSurfaceReviewerAlias(reviewer) {
179
+ const activeRoles = reviewerRolesFor(resolveToolkitSurface());
180
+ for (const roles of Object.values(REVIEWER_ROLES_BY_SURFACE)) {
181
+ if (reviewer === roles.codeReview)
182
+ return activeRoles.codeReview;
183
+ if (reviewer === roles.manualQa)
184
+ return activeRoles.manualQa;
185
+ if (reviewer === roles.gateReview)
186
+ return activeRoles.gateReview;
187
+ }
188
+ return reviewer;
189
+ }
190
+ function withExclusiveLock(lockPath, fn) {
191
+ mkdirSync(dirname(lockPath), { recursive: true });
192
+ const maxAttempts = 10;
193
+ const baseDelayMs = 10;
194
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
195
+ let fd = null;
196
+ try {
197
+ fd = openSync(lockPath, "wx");
198
+ writeFileSync(fd, process.pid.toString());
199
+ try {
200
+ return fn();
201
+ }
202
+ finally {
203
+ try {
204
+ unlinkSync(lockPath);
205
+ }
206
+ catch {
207
+ /* empty */
208
+ }
209
+ }
210
+ }
211
+ catch (error) {
212
+ if (fd !== null) {
213
+ try {
214
+ unlinkSync(lockPath);
215
+ }
216
+ catch {
217
+ /* empty */
218
+ }
219
+ }
220
+ if (error.code !== "EEXIST") {
221
+ return fn();
222
+ }
223
+ const delayMs = baseDelayMs * 2 ** attempt + Math.random() * baseDelayMs;
224
+ const deadline = Date.now() + delayMs;
225
+ while (Date.now() < deadline) {
226
+ /* spin */
227
+ }
228
+ }
229
+ }
230
+ return fn();
231
+ }
232
+ function atomicWriteJson(targetPath, data) {
233
+ const tmp = join(dirname(targetPath), `.tmp-${randomBytes(6).toString("hex")}`);
234
+ writeFileSync(tmp, JSON.stringify(data));
235
+ renameSync(tmp, targetPath);
90
236
  }
91
237
  function deny(reason) {
92
238
  return `${JSON.stringify({
@@ -105,6 +251,13 @@ function fanOutLimit() {
105
251
  const parsed = Number.parseInt(raw, 10);
106
252
  return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_FANOUT_LIMIT;
107
253
  }
254
+ function reviewSpawnLimit() {
255
+ const raw = process.env["OMO_ULW_LOOP_REVIEW_SPAWN_LIMIT"];
256
+ if (raw === undefined)
257
+ return DEFAULT_REVIEW_SPAWN_LIMIT;
258
+ const parsed = Number.parseInt(raw, 10);
259
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_REVIEW_SPAWN_LIMIT;
260
+ }
108
261
  function isNonEmptyFile(path) {
109
262
  try {
110
263
  return existsSync(path) && statSync(path).size > 0;
@@ -136,6 +289,24 @@ function readCount(counterPath) {
136
289
  throw error;
137
290
  }
138
291
  }
292
+ function readCounts(counterPath) {
293
+ try {
294
+ const parsed = JSON.parse(readFileSync(counterPath, "utf8"));
295
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
296
+ return {};
297
+ const counts = {};
298
+ for (const [key, value] of Object.entries(parsed)) {
299
+ if (typeof value === "number" && value >= 0)
300
+ counts[key] = value;
301
+ }
302
+ return counts;
303
+ }
304
+ catch (error) {
305
+ if (error instanceof Error)
306
+ return {};
307
+ throw error;
308
+ }
309
+ }
139
310
  function readPlan(goalsPath) {
140
311
  try {
141
312
  return JSON.parse(readFileSync(goalsPath, "utf8"));
@@ -0,0 +1,6 @@
1
+ import type { UlwLoopPlan } from "./types.js";
2
+ /**
3
+ * `status --json` is the one call every agent already makes between steps, so it is
4
+ * also the cheapest place to answer "what now?" without a second round trip.
5
+ */
6
+ export declare function statusNextActions(plan: UlwLoopPlan): readonly string[];
@@ -0,0 +1,33 @@
1
+ import { hasAllCriteriaPass, isFinalRunCompletionCandidate } from "./goal-status.js";
2
+ import { ULW_LOOP_CREATE_GOALS_COMMAND } from "./plan-missing-recovery.js";
3
+ /**
4
+ * `status --json` is the one call every agent already makes between steps, so it is
5
+ * also the cheapest place to answer "what now?" without a second round trip.
6
+ */
7
+ export function statusNextActions(plan) {
8
+ const actions = [];
9
+ const active = plan.goals.find((goal) => goal.id === plan.activeGoalId);
10
+ if (plan.goals.length === 0)
11
+ actions.push(`No goals yet: bootstrap with \`${ULW_LOOP_CREATE_GOALS_COMMAND}\`.`);
12
+ else if (active !== undefined)
13
+ actions.push(...activeGoalActions(plan, active));
14
+ for (const goal of plan.goals.filter((candidate) => candidate.status === "review_blocked"))
15
+ actions.push(`${goal.id} is review_blocked: capture the reviewer verdict with \`omo-agent-toolkit ulw-loop record-review-blockers --goal-id ${goal.id} --title "<title>" --objective "<objective>" --evidence "<verdict>" --codex-goal-json '<get_goal json>'\`.`);
16
+ if (plan.evidenceLayoutVersion !== 2 || active === undefined)
17
+ actions.push("plan is evidence-layout v1; artifacts go under .omo/evidence/");
18
+ return actions;
19
+ }
20
+ function activeGoalActions(plan, active) {
21
+ const unresolved = active.successCriteria.filter((criterion) => criterion.status !== "pass");
22
+ if (unresolved.length > 0)
23
+ return [
24
+ `${active.id} has ${unresolved.length} unresolved criterion(s) (${unresolved.map((criterion) => criterion.id).join(", ")}): record proof with \`omo-agent-toolkit ulw-loop record-evidence --goal-id ${active.id} --criterion-id <id> --status pass --evidence "<observable proof>"\`.`,
25
+ ];
26
+ if (hasAllCriteriaPass(active) && isFinalRunCompletionCandidate(plan, active))
27
+ return [
28
+ `${active.id} passes every criterion and is the final story: update_goal complete, then checkpoint --print-template to build the final quality gate.`,
29
+ ];
30
+ return [
31
+ `${active.id} passes every criterion: close it with \`omo-agent-toolkit ulw-loop checkpoint --goal-id ${active.id} --status complete --evidence "<proof>" --codex-goal-json '<get_goal json>'\`.`,
32
+ ];
33
+ }
@@ -0,0 +1,18 @@
1
+ export type UlwLoopToolkitSurface = "lazycodex" | "omo-senpi";
2
+ export interface UlwLoopReviewerRoles {
3
+ readonly codeReview: string;
4
+ readonly manualQa: string;
5
+ readonly gateReview: string;
6
+ }
7
+ export declare const REVIEWER_ROLES_BY_SURFACE: Readonly<Record<UlwLoopToolkitSurface, UlwLoopReviewerRoles>>;
8
+ export declare const GATE_REVIEWER_AGENT_NAMES: ReadonlySet<string>;
9
+ export type UlwLoopGateSection = "codeReview" | "manualQa" | "gateReview" | "iteration" | "criteriaCoverage";
10
+ export declare const REQUIRED_GATE_SECTIONS_BY_SURFACE: Readonly<Record<UlwLoopToolkitSurface, readonly UlwLoopGateSection[]>>;
11
+ export declare const GATE_SECTION_BY_ACCEPTOR: Readonly<Record<UlwLoopToolkitSurface, Readonly<Partial<Record<UlwLoopGateSection, readonly string[]>>>>>;
12
+ export declare function reviewerRolesFor(surface: UlwLoopToolkitSurface): UlwLoopReviewerRoles;
13
+ export declare const SURFACE_MARKER_FILENAME = "surface.json";
14
+ export interface ResolveToolkitSurfaceOptions {
15
+ readonly env?: Readonly<Record<string, string | undefined>>;
16
+ readonly entryDir?: string;
17
+ }
18
+ export declare function resolveToolkitSurface(options?: ResolveToolkitSurfaceOptions): UlwLoopToolkitSurface;
@@ -0,0 +1,65 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ export const REVIEWER_ROLES_BY_SURFACE = {
5
+ lazycodex: {
6
+ codeReview: "lazycodex-code-reviewer",
7
+ manualQa: "lazycodex-qa-executor",
8
+ gateReview: "lazycodex-gate-reviewer",
9
+ },
10
+ "omo-senpi": {
11
+ codeReview: "omo-senpi-code-reviewer",
12
+ manualQa: "omo-senpi-qa-executor",
13
+ gateReview: "omo-senpi-gate-reviewer",
14
+ },
15
+ };
16
+ export const GATE_REVIEWER_AGENT_NAMES = new Set(Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.gateReview));
17
+ export const REQUIRED_GATE_SECTIONS_BY_SURFACE = {
18
+ lazycodex: ["codeReview", "manualQa", "gateReview", "iteration", "criteriaCoverage"],
19
+ "omo-senpi": ["manualQa", "gateReview", "iteration", "criteriaCoverage"],
20
+ };
21
+ export const GATE_SECTION_BY_ACCEPTOR = {
22
+ lazycodex: {
23
+ codeReview: [REVIEWER_ROLES_BY_SURFACE.lazycodex.codeReview],
24
+ manualQa: [REVIEWER_ROLES_BY_SURFACE.lazycodex.manualQa],
25
+ gateReview: [REVIEWER_ROLES_BY_SURFACE.lazycodex.gateReview],
26
+ },
27
+ "omo-senpi": {
28
+ manualQa: ["main-session"],
29
+ gateReview: ["category:deep", "category:unspecified-high", "category:unspecified-low"],
30
+ },
31
+ };
32
+ export function reviewerRolesFor(surface) {
33
+ return REVIEWER_ROLES_BY_SURFACE[surface];
34
+ }
35
+ export const SURFACE_MARKER_FILENAME = "surface.json";
36
+ const SURFACE_ENV_KEY = "OMO_AGENT_TOOLKIT_SURFACE";
37
+ function parseSurface(value) {
38
+ return value === "lazycodex" || value === "omo-senpi" ? value : null;
39
+ }
40
+ // Resolution order: explicit env override (tests, doctor probes) -> staged surface.json marker
41
+ // sitting next to the running bundle -> lazycodex default. The marker is distribution-baked, so a
42
+ // malformed marker falls back to the default instead of crashing checkpoint validation; the env
43
+ // override remains available to force the intended surface on a damaged install.
44
+ export function resolveToolkitSurface(options) {
45
+ const env = options?.env ?? process.env;
46
+ const fromEnv = parseSurface(env[SURFACE_ENV_KEY]);
47
+ if (fromEnv !== null)
48
+ return fromEnv;
49
+ const entryDir = options?.entryDir ?? dirname(fileURLToPath(import.meta.url));
50
+ const markerPath = join(entryDir, SURFACE_MARKER_FILENAME);
51
+ return readMarkerSurface(markerPath) ?? "lazycodex";
52
+ }
53
+ function readMarkerSurface(markerPath) {
54
+ try {
55
+ if (!existsSync(markerPath))
56
+ return null;
57
+ const parsed = JSON.parse(readFileSync(markerPath, "utf8"));
58
+ return parseSurface(parsed["surface"]);
59
+ }
60
+ catch (error) {
61
+ if (error instanceof Error)
62
+ return null;
63
+ throw error;
64
+ }
65
+ }
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit --with-ultrawork",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.30) Checking Ulw-Loop Steering"
10
+ "statusMessage": "(OmO 5.0.0-beta.32) Checking Ulw-Loop Steering"
11
11
  }
12
12
  ]
13
13
  }
@@ -20,7 +20,7 @@
20
20
  "type": "command",
21
21
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use",
22
22
  "timeout": 5,
23
- "statusMessage": "(OmO 5.0.0-beta.30) Enforcing Unlimited Ulw-Loop Budget"
23
+ "statusMessage": "(OmO 5.0.0-beta.32) Enforcing Unlimited Ulw-Loop Budget"
24
24
  }
25
25
  ]
26
26
  },
@@ -31,7 +31,7 @@
31
31
  "type": "command",
32
32
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use-spawn",
33
33
  "timeout": 5,
34
- "statusMessage": "(OmO 5.0.0-beta.30) Guarding Ulw-Loop Spawns"
34
+ "statusMessage": "(OmO 5.0.0-beta.32) Guarding Ulw-Loop Spawns"
35
35
  }
36
36
  ]
37
37
  }
@@ -43,7 +43,7 @@
43
43
  "type": "command",
44
44
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook stop",
45
45
  "timeout": 10,
46
- "statusMessage": "(OmO 5.0.0-beta.30) Checking Ulw-Loop Resume"
46
+ "statusMessage": "(OmO 5.0.0-beta.32) Checking Ulw-Loop Resume"
47
47
  }
48
48
  ]
49
49
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-ulw-loop",
3
- "version": "5.0.0-beta.30",
3
+ "version": "5.0.0-beta.32",
4
4
  "description": "Codex plugin: durable repo-native multi-goal orchestration with embedded success criteria and observable evidence audit.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -179,6 +179,23 @@ Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures a
179
179
  4. If blocked or failed, checkpoint with `--status blocked` or `--status failed` and include diagnosis evidence.
180
180
  5. If this is the final goal, run the final quality gate first and pass `--quality-gate-json`.
181
181
 
182
+ ## Exact final-story sequence
183
+ For the final story, follow this exact checkpoint sequence:
184
+
185
+ ```sh
186
+ omo-agent-toolkit ulw-loop status --json
187
+ # Read nextActions and currentAttemptDir.
188
+ omo-agent-toolkit ulw-loop record-evidence --goal-id <g> --criterion-id <c> --status pass --evidence "..."
189
+ # Repeat record-evidence once per criterion.
190
+ # Then use the harness update_goal tool with status complete.
191
+ omo-agent-toolkit ulw-loop checkpoint --goal-id <g> --print-template --json
192
+ # Fill the printed template: replace every placeholder and use real artifact paths under currentAttemptDir.
193
+ omo-agent-toolkit ulw-loop checkpoint --goal-id <g> --status complete --evidence "..." --codex-goal-json <path> --quality-gate-json <path>
194
+ omo-agent-toolkit ulw-loop complete-goals
195
+ ```
196
+
197
+ The lazycodex gate uses all five sections shown in the sample below, including `codeReview`.
198
+
182
199
  ## Final Quality Gate
183
200
  Trigger only for the final aggregate goal after every criterion in every goal is `pass`.
184
201
  1. Run targeted verification for changed behavior.
@@ -1,4 +1,5 @@
1
1
  import { type CheckpointUlwLoopArgs, type CheckpointUlwLoopResult, checkpointUlwLoop } from "./checkpoint.js";
2
+ import { checkpointTemplate } from "./checkpoint-template.js";
2
3
  import { hasFlag, parseCodexGoalJson, readValue } from "./cli-arg-parser.js";
3
4
  import { blockedDecisionHandoff, printJson } from "./cli-output.js";
4
5
  import { buildCodexGoalInstruction, type UlwLoopGoalInstruction } from "./codex-goal-instruction.js";
@@ -32,6 +33,12 @@ export async function checkpoint(
32
33
  json: boolean,
33
34
  scope?: UlwLoopScope,
34
35
  ): Promise<number> {
36
+ if (hasFlag(argv, "--print-template")) {
37
+ const template = await checkpointTemplate(repoRoot, scope, readValue(argv, "--goal-id"));
38
+ if (json) printJson({ ok: true, ...template });
39
+ else printJson(template);
40
+ return 0;
41
+ }
35
42
  const goalId = required(argv, "--goal-id");
36
43
  const statusValue = checkpointStatus(required(argv, "--status"));
37
44
  const evidence = required(argv, "--evidence");
@@ -1,10 +1,34 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { readFile } from "node:fs/promises";
3
3
 
4
+ import type { CodexGoalReconciliation, CodexGoalSnapshot } from "./codex-goal-snapshot.js";
5
+ import { codexGoalMismatchRecovery, formatCodexGoalReconciliation } from "./codex-goal-snapshot.js";
4
6
  import { codexGoalMode, isFinalRunCompletionCandidate } from "./goal-status.js";
5
7
  import { type UlwLoopScope, ulwLoopBriefPath } from "./paths.js";
6
8
  import type { UlwLoopItem, UlwLoopPlan } from "./types.js";
7
- import { ULW_LOOP_DIR, ULW_LOOP_GOALS, ULW_LOOP_LEDGER } from "./types.js";
9
+ import { ULW_LOOP_DIR, ULW_LOOP_GOALS, ULW_LOOP_LEDGER, UlwLoopError } from "./types.js";
10
+
11
+ export interface CodexSnapshotMismatchInput {
12
+ readonly reconciliation: CodexGoalReconciliation;
13
+ readonly snapshot: CodexGoalSnapshot | null | undefined;
14
+ readonly expectedObjective: string;
15
+ /** Only the checkpoint path can fall back to task-scoped aggregate reconciliation, so only it asks for that hint. */
16
+ readonly taskScopedHint?: { readonly goal: UlwLoopItem; readonly aggregate: boolean; readonly final: boolean };
17
+ }
18
+
19
+ export function codexSnapshotMismatchError(input: CodexSnapshotMismatchInput): UlwLoopError {
20
+ const { reconciliation, snapshot, taskScopedHint } = input;
21
+ const hint =
22
+ taskScopedHint?.aggregate === true && snapshot?.status === "complete" && snapshot.objective !== undefined
23
+ ? buildTaskScopedAggregateReconciliationHint(taskScopedHint.goal, taskScopedHint.final)
24
+ : "";
25
+ const recovery = codexGoalMismatchRecovery(input.expectedObjective, snapshot);
26
+ return new UlwLoopError(
27
+ `${formatCodexGoalReconciliation(reconciliation)}${hint}\n${recovery.message}`,
28
+ "ulw_loop_codex_snapshot_mismatch",
29
+ { details: recovery.details },
30
+ );
31
+ }
8
32
 
9
33
  function normalizeObjective(value: string): string {
10
34
  return value.replace(/\s+/g, " ").trim();
@@ -0,0 +1,127 @@
1
+ import { type UlwLoopScope, ulwLoopAttemptEvidenceDir } from "./paths.js";
2
+ import { readUlwLoopPlan } from "./plan-io.js";
3
+ import { resolveToolkitSurface, type UlwLoopToolkitSurface } from "./surface.js";
4
+ import type { UlwLoopPlan } from "./types.js";
5
+ import { UlwLoopError } from "./types.js";
6
+
7
+ export interface CheckpointTemplate {
8
+ readonly qualityGateTemplate: Record<string, unknown>;
9
+ readonly codexGoalTemplate: Record<string, unknown>;
10
+ readonly attemptDir?: string;
11
+ readonly guidance?: string;
12
+ }
13
+
14
+ function artifactPath(base: string, name: string): string {
15
+ return `${base}/${name}`;
16
+ }
17
+
18
+ function gateTemplate(surface: UlwLoopToolkitSurface, base: string): Record<string, unknown> {
19
+ const artifacts = [
20
+ {
21
+ id: "artifact-cli",
22
+ kind: "cli-transcript",
23
+ description: "<replace:artifact description>",
24
+ path: artifactPath(base, "cli-transcript.txt"),
25
+ },
26
+ {
27
+ id: "artifact-data",
28
+ kind: "data-diff",
29
+ description: "<replace:artifact description>",
30
+ path: artifactPath(base, "data-diff.txt"),
31
+ },
32
+ ];
33
+ const manualQa = {
34
+ by: surface === "omo-senpi" ? "main-session" : "lazycodex-qa-executor",
35
+ status: "passed",
36
+ evidence: "<replace:manual QA evidence>",
37
+ surfaceEvidence: [
38
+ {
39
+ id: "surface-cli",
40
+ criterionRef: "<replace:criterion id>",
41
+ surface: "cli",
42
+ invocation: "<replace:command>",
43
+ verdict: "passed",
44
+ artifactRefs: ["artifact-cli"],
45
+ },
46
+ {
47
+ id: "surface-data",
48
+ criterionRef: "<replace:criterion id>",
49
+ surface: "data",
50
+ invocation: "<replace:command>",
51
+ verdict: "passed",
52
+ artifactRefs: ["artifact-data"],
53
+ },
54
+ ],
55
+ adversarialCases: [
56
+ {
57
+ id: "<replace:adversarial case id>",
58
+ criterionRef: "<replace:criterion id>",
59
+ scenario: "<replace:scenario>",
60
+ expectedBehavior: "<replace:expected behavior>",
61
+ verdict: "not_applicable",
62
+ reason: "<replace:reason>",
63
+ artifactRefs: ["artifact-cli"],
64
+ },
65
+ ],
66
+ artifactRefs: artifacts,
67
+ };
68
+ const common = {
69
+ manualQa,
70
+ gateReview: {
71
+ by: surface === "omo-senpi" ? "category:deep" : "lazycodex-gate-reviewer",
72
+ recommendation: "APPROVE",
73
+ reportPath: artifactPath(base, "gate-review.md"),
74
+ evidence: "<replace:gate review evidence>",
75
+ blockers: [],
76
+ },
77
+ iteration: {
78
+ fullRerun: true,
79
+ status: "passed",
80
+ rerunCommands: ["<replace:verification command>"],
81
+ evidence: "<replace:iteration evidence>",
82
+ },
83
+ criteriaCoverage: {
84
+ totalCriteria: 0,
85
+ passCount: 0,
86
+ originalIntent: "<replace:original intent>",
87
+ desiredOutcome: "<replace:desired outcome>",
88
+ userOutcomeReview: "<replace:user outcome review>",
89
+ adversarialClassesCovered: ["<replace:adversarial class>"],
90
+ },
91
+ };
92
+ if (surface === "omo-senpi") return common;
93
+ return {
94
+ codeReview: {
95
+ by: "lazycodex-code-reviewer",
96
+ recommendation: "APPROVE",
97
+ codeQualityStatus: "CLEAR",
98
+ reportPath: artifactPath(base, "code-review.md"),
99
+ evidence: "<replace:code review evidence>",
100
+ blockers: [],
101
+ },
102
+ ...common,
103
+ };
104
+ }
105
+
106
+ export async function checkpointTemplate(
107
+ repoRoot: string,
108
+ scope?: UlwLoopScope,
109
+ goalId?: string,
110
+ ): Promise<CheckpointTemplate> {
111
+ const plan: UlwLoopPlan = await readUlwLoopPlan(repoRoot, scope);
112
+ const targetId = goalId ?? plan.activeGoalId;
113
+ const active = plan.goals.find((goal) => goal.id === targetId);
114
+ if (goalId !== undefined && active === undefined)
115
+ throw new UlwLoopError(`Unknown ulw-loop id: ${goalId}.`, "ULW_LOOP_GOAL_NOT_FOUND", { details: { goalId } });
116
+ const hasAttempt = plan.evidenceLayoutVersion === 2 && active !== undefined;
117
+ const attemptDir = hasAttempt ? ulwLoopAttemptEvidenceDir(active.id, active.attempt, scope) : ".omo/evidence";
118
+ return {
119
+ qualityGateTemplate: gateTemplate(resolveToolkitSurface(), attemptDir),
120
+ codexGoalTemplate: {
121
+ goal: { objective: plan.codexObjective ?? "<replace:codex objective>", status: "complete" },
122
+ },
123
+ ...(hasAttempt
124
+ ? { attemptDir }
125
+ : { guidance: "This plan is evidence-layout v1; artifacts go under .omo/evidence/." }),
126
+ };
127
+ }