oh-my-opencode 4.19.1 → 4.19.3

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 (268) hide show
  1. package/.agents/command/publish.md +95 -28
  2. package/.agents/skills/publish/SKILL.md +20 -6
  3. package/.opencode/command/publish.md +95 -28
  4. package/README.ja.md +2 -2
  5. package/README.ko.md +2 -2
  6. package/README.md +2 -2
  7. package/README.ru.md +2 -2
  8. package/README.zh-cn.md +2 -2
  9. package/THIRD-PARTY-NOTICES.md +7 -7
  10. package/dist/agents/atlas/agent.d.ts +4 -2
  11. package/dist/agents/explore.d.ts +4 -2
  12. package/dist/agents/hephaestus/agent.d.ts +4 -2
  13. package/dist/agents/librarian.d.ts +4 -2
  14. package/dist/agents/metis.d.ts +4 -2
  15. package/dist/agents/momus.d.ts +3 -2
  16. package/dist/agents/multimodal-looker.d.ts +4 -2
  17. package/dist/agents/oracle.d.ts +4 -2
  18. package/dist/agents/sisyphus/claude-opus-5.d.ts +33 -0
  19. package/dist/agents/sisyphus/index.d.ts +2 -0
  20. package/dist/agents/sisyphus-agent-factory.d.ts +5 -2
  21. package/dist/agents/sisyphus-junior/agent.d.ts +4 -1
  22. package/dist/agents/sisyphus-runtime-prompt-reconciler.d.ts +1 -1
  23. package/dist/agents/types.d.ts +2 -2
  24. package/dist/cli/config-migrate.d.ts +8 -0
  25. package/dist/cli/doctor/checks/legacy-config-leftovers.d.ts +10 -0
  26. package/dist/cli/doctor/checks/model-resolution-config.d.ts +1 -1
  27. package/dist/cli/doctor/checks/tools-lsp.d.ts +1 -1
  28. package/dist/cli/doctor/framework/constants.d.ts +1 -1
  29. package/dist/cli/index.js +95391 -88524
  30. package/dist/cli-node/index.js +95504 -88637
  31. package/dist/config/schema/agent-names.d.ts +26 -26
  32. package/dist/config/schema/agent-overrides.d.ts +400 -400
  33. package/dist/config/schema/browser-automation.d.ts +2 -2
  34. package/dist/config/schema/categories.d.ts +36 -36
  35. package/dist/config/schema/commands.d.ts +2 -2
  36. package/dist/config/schema/dynamic-context-pruning.d.ts +1 -1
  37. package/dist/config/schema/experimental.d.ts +1 -1
  38. package/dist/config/schema/fallback-models.d.ts +25 -25
  39. package/dist/config/schema/hooks.d.ts +43 -43
  40. package/dist/config/schema/internal/permission.d.ts +9 -9
  41. package/dist/config/schema/keyword-detector.d.ts +6 -6
  42. package/dist/config/schema/oh-my-opencode-config.d.ts +402 -402
  43. package/dist/config/schema/tmux.d.ts +6 -6
  44. package/dist/config/validate.d.ts +2 -1
  45. package/dist/config-migration/deep-diff.d.ts +1 -0
  46. package/dist/config-migration/discovery-paths.d.ts +11 -0
  47. package/dist/config-migration/discovery-roots.d.ts +7 -0
  48. package/dist/config-migration/discovery.d.ts +4 -0
  49. package/dist/config-migration/index.d.ts +9 -0
  50. package/dist/config-migration/legacy-history.d.ts +3 -0
  51. package/dist/config-migration/migration-executor.d.ts +4 -0
  52. package/dist/config-migration/migration-plans.d.ts +14 -0
  53. package/dist/config-migration/record-values.d.ts +4 -0
  54. package/dist/config-migration/schema-url.d.ts +1 -0
  55. package/dist/config-migration/transform-config-jsonc.d.ts +2 -0
  56. package/dist/config-migration/transform-opencode.d.ts +2 -0
  57. package/dist/config-migration/transform-types.d.ts +24 -0
  58. package/dist/config-migration/types.d.ts +39 -0
  59. package/dist/create-hooks.d.ts +35 -35
  60. package/dist/features/background-agent/spawner/tmux-callback-invoker.d.ts +11 -0
  61. package/dist/features/builtin-commands/templates/hyperplan.d.ts +1 -1
  62. package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
  63. package/dist/features/claude-tasks/types.d.ts +4 -4
  64. package/dist/features/team-mode/team-runtime/shutdown-helpers.d.ts +1 -1
  65. package/dist/features/team-mode/team-runtime/shutdown-test-fixtures.d.ts +4 -4
  66. package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +42 -42
  67. package/dist/features/tmux-subagent/environment-eligibility.d.ts +2 -0
  68. package/dist/features/tmux-subagent/index.d.ts +1 -0
  69. package/dist/features/tmux-subagent/tracked-session-state.d.ts +1 -0
  70. package/dist/features/tui-sidebar/snapshot-schema.d.ts +6 -6
  71. package/dist/help/schema/acp.d.ts +3 -3
  72. package/dist/help/schema/doctor.d.ts +4 -4
  73. package/dist/help/schema/status.d.ts +3 -3
  74. package/dist/hooks/auto-update-checker/constants.d.ts +3 -3
  75. package/dist/hooks/goal/types.d.ts +5 -5
  76. package/dist/hooks/non-interactive-env/constants.d.ts +4 -1
  77. package/dist/hooks/start-work/parse-user-request.d.ts +2 -0
  78. package/dist/hooks/start-work/worktree-block.d.ts +5 -0
  79. package/dist/index.js +35450 -28257
  80. package/dist/mcp/types.d.ts +3 -3
  81. package/dist/oh-my-opencode.schema.json +1 -1
  82. package/dist/plugin/chat-message/loop-commands.d.ts +1 -0
  83. package/dist/plugin/command-execute-before.d.ts +3 -3
  84. package/dist/plugin/hooks/create-core-hooks.d.ts +25 -25
  85. package/dist/plugin-config/omo-config-chain.d.ts +14 -0
  86. package/dist/plugin-config/unknown-key-diagnostics.d.ts +5 -0
  87. package/dist/plugin-config.d.ts +2 -2
  88. package/dist/shared/agent-display-names.d.ts +1 -1
  89. package/dist/shared/jsonc-parser.d.ts +1 -1
  90. package/dist/shared/migration.d.ts +5 -1
  91. package/dist/shared/opencode-config-dir-types.d.ts +0 -1
  92. package/dist/shared/plugin-identity.d.ts +6 -8
  93. package/dist/shared/project-discovery-dirs.d.ts +0 -1
  94. package/dist/shared/tmux/tmux-utils/environment.d.ts +1 -1
  95. package/dist/shared/tmux/tmux-utils.d.ts +1 -1
  96. package/dist/shared/typescript-native-source-parser.d.ts +7 -0
  97. package/dist/skills/coding-agent-sessions/SKILL.md +5 -2
  98. package/dist/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  99. package/dist/skills/coding-agent-sessions/references/senpi.md +15 -5
  100. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  101. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  102. package/dist/skills/debugging/SKILL.md +2 -1
  103. package/dist/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  104. package/dist/skills/frontend/ATTRIBUTION.md +7 -0
  105. package/dist/skills/frontend/SKILL.md +4 -1
  106. package/dist/skills/frontend/references/design/README.md +8 -0
  107. package/dist/skills/frontend/references/design/_INDEX.md +14 -1
  108. package/dist/skills/frontend/references/design/interaction-skill.md +144 -0
  109. package/dist/skills/programming/SKILL.md +11 -1
  110. package/dist/skills/programming/references/logging.md +6 -1
  111. package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +51 -17
  112. package/dist/skills/start-work/SKILL.md +12 -2
  113. package/dist/skills/ulw-plan/SKILL.md +1 -1
  114. package/dist/skills/ulw-plan/references/full-workflow.md +12 -1
  115. package/dist/skills/ulw-plan/references/intent-clear.md +1 -1
  116. package/dist/startup-migration.d.ts +28 -0
  117. package/dist/testing/create-plugin-module.d.ts +4 -0
  118. package/dist/tools/task/types.d.ts +10 -10
  119. package/dist/tui.js +31812 -26849
  120. package/package.json +14 -14
  121. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +21 -5
  122. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +11 -2
  123. package/packages/omo-codex/THIRD-PARTY-NOTICES.md +2 -2
  124. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  125. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +8 -4
  126. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  127. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  128. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +11 -5
  129. package/packages/omo-codex/plugin/components/codegraph/NOTICE +1 -1
  130. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +10272 -2600
  131. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +7341 -562
  132. package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
  133. package/packages/omo-codex/plugin/components/codegraph/src/hook-input.ts +33 -0
  134. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +36 -10
  135. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +170 -186
  136. package/packages/omo-codex/plugin/components/codegraph/src/post-tool-use-hook.ts +34 -0
  137. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +22 -5
  138. package/packages/omo-codex/plugin/components/codegraph/src/session-start-command.ts +106 -0
  139. package/packages/omo-codex/plugin/components/codegraph/src/session-start-cooldown.ts +145 -0
  140. package/packages/omo-codex/plugin/components/codegraph/src/session-start-hook-runtime.ts +21 -0
  141. package/packages/omo-codex/plugin/components/codegraph/src/session-start-lock.ts +139 -0
  142. package/packages/omo-codex/plugin/components/codegraph/src/session-start-outcome.ts +15 -0
  143. package/packages/omo-codex/plugin/components/codegraph/src/session-start-paths.ts +32 -0
  144. package/packages/omo-codex/plugin/components/codegraph/src/session-start-project.ts +109 -0
  145. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker-result.ts +148 -0
  146. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +126 -186
  147. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +9 -4
  148. package/packages/omo-codex/plugin/components/codegraph/test/hook-session-start-guard.test.ts +160 -0
  149. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +12 -110
  150. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +29 -20
  151. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +3 -3
  152. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +2 -2
  153. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +11 -5
  154. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -3
  155. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +2 -1
  156. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +50 -5
  157. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +3 -2
  158. package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +24 -13
  159. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +70 -13
  160. package/packages/omo-codex/plugin/components/codegraph/test/session-start-project.test.ts +58 -0
  161. package/packages/omo-codex/plugin/components/codegraph/test/session-start-state.test.ts +104 -0
  162. package/packages/omo-codex/plugin/components/codegraph/test/session-start-trust-boundary.test.ts +4 -2
  163. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-cooldown.test.ts +116 -0
  164. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +114 -119
  165. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  166. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  167. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  168. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  169. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  170. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  171. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  172. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  173. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  174. package/packages/omo-codex/plugin/components/rules/dist/cli.js +31 -14
  175. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  176. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  177. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +1 -1
  178. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  179. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  180. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  181. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  182. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  183. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  184. package/packages/omo-codex/plugin/components/ultrawork/directive.md +12 -7
  185. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  186. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  187. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +12 -7
  188. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +1 -1
  189. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +12 -1
  190. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +1 -1
  191. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +12 -7
  192. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  193. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  194. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +19 -1
  195. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  196. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  197. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  198. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  199. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  200. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  201. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  202. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  203. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  204. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  205. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  206. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  207. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  208. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  209. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  210. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  211. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  212. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  213. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  214. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  215. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  216. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  217. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  218. package/packages/omo-codex/plugin/package-lock.json +42 -41
  219. package/packages/omo-codex/plugin/package.json +1 -1
  220. package/packages/omo-codex/plugin/shared/src/config-loader.ts +165 -16
  221. package/packages/omo-codex/plugin/shared/src/config-migration.ts +162 -0
  222. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +196 -18
  223. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +5 -2
  224. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  225. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/senpi.md +15 -5
  226. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  227. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  228. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -1
  229. package/packages/omo-codex/plugin/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  230. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +7 -0
  231. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +4 -1
  232. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +8 -0
  233. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +14 -1
  234. package/packages/omo-codex/plugin/skills/frontend/references/design/interaction-skill.md +144 -0
  235. package/packages/omo-codex/plugin/skills/programming/SKILL.md +11 -1
  236. package/packages/omo-codex/plugin/skills/programming/references/logging.md +6 -1
  237. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +51 -17
  238. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +12 -2
  239. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +12 -7
  240. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +19 -1
  241. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +1 -1
  242. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +12 -1
  243. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +1 -1
  244. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +22 -0
  245. package/packages/omo-codex/scripts/install-dist/install-local.mjs +1 -1
  246. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +5 -2
  247. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  248. package/packages/shared-skills/skills/coding-agent-sessions/references/senpi.md +15 -5
  249. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +46 -0
  250. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +19 -10
  251. package/packages/shared-skills/skills/debugging/SKILL.md +2 -1
  252. package/packages/shared-skills/skills/debugging/references/methodology/03-flaky-triage.md +47 -0
  253. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +7 -0
  254. package/packages/shared-skills/skills/frontend/SKILL.md +4 -1
  255. package/packages/shared-skills/skills/frontend/references/design/README.md +8 -0
  256. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +14 -1
  257. package/packages/shared-skills/skills/frontend/references/design/interaction-skill.md +144 -0
  258. package/packages/shared-skills/skills/programming/SKILL.md +11 -1
  259. package/packages/shared-skills/skills/programming/references/logging.md +6 -1
  260. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +51 -17
  261. package/packages/shared-skills/skills/start-work/SKILL.md +12 -2
  262. package/packages/shared-skills/skills/ulw-plan/SKILL.md +1 -1
  263. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +12 -1
  264. package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +1 -1
  265. package/dist/plugin-config/layered-config-loader.d.ts +0 -2
  266. package/dist/plugin-config/single-config-loader.d.ts +0 -4
  267. package/dist/shared/migrate-legacy-config-file.d.ts +0 -1
  268. package/dist/shared/migration/config-migration.d.ts +0 -1
@@ -0,0 +1,148 @@
1
+ import { env as processEnv, stderr as processStderr } from "node:process";
2
+
3
+ import type { CodegraphCommandResolution } from "../../../../../utils/src/codegraph/resolve.ts";
4
+ import type { CodegraphSessionStartOutcome, WorkerAction } from "./hook-types.js";
5
+ import {
6
+ clearSessionStartCooldown,
7
+ recordSessionStartFailure,
8
+ type SessionStartFailureAction,
9
+ } from "./session-start-cooldown.js";
10
+ import type { CodegraphProjectState } from "./session-start-project.js";
11
+
12
+ export type WorkerResultContext = {
13
+ readonly baseCooldownMs: number;
14
+ readonly homeDir: string;
15
+ readonly logOutcome: (outcome: CodegraphSessionStartOutcome) => void;
16
+ readonly nowMs: () => number;
17
+ readonly projectRoot: string;
18
+ };
19
+
20
+ export type WorkerRunResult = {
21
+ readonly action: WorkerAction;
22
+ readonly releaseLock: boolean;
23
+ };
24
+
25
+ export function finishWorkerProjectState(
26
+ state: CodegraphProjectState,
27
+ context: WorkerResultContext,
28
+ ): WorkerRunResult | null {
29
+ switch (state.kind) {
30
+ case "inconclusive":
31
+ return finishWorkerFailure("skipped-probe-error", "skipped-probe-error", {
32
+ error: state.reason,
33
+ projectRoot: context.projectRoot,
34
+ }, context);
35
+ case "initialized":
36
+ return finishWorkerSuccess("skipped-initialized", { projectRoot: context.projectRoot }, context);
37
+ case "nested-root":
38
+ return finishWorkerSuccess("skipped-nested-root", {
39
+ ancestorRoot: state.ancestorRoot,
40
+ projectRoot: context.projectRoot,
41
+ }, context);
42
+ case "uninitialized":
43
+ return null;
44
+ }
45
+ }
46
+
47
+ export function finishWorkerInitialization(
48
+ state: CodegraphProjectState,
49
+ detail: {
50
+ readonly exitCode: number;
51
+ readonly source: CodegraphCommandResolution["source"];
52
+ },
53
+ context: WorkerResultContext,
54
+ ): WorkerRunResult {
55
+ switch (state.kind) {
56
+ case "inconclusive":
57
+ return finishWorkerFailure("skipped-probe-error", "skipped-probe-error", {
58
+ error: state.reason,
59
+ ...detail,
60
+ projectRoot: context.projectRoot,
61
+ timedOut: false,
62
+ }, context);
63
+ case "initialized":
64
+ return finishWorkerSuccess("initialized", {
65
+ ...detail,
66
+ projectRoot: context.projectRoot,
67
+ timedOut: false,
68
+ }, context);
69
+ case "nested-root":
70
+ return finishWorkerFailure("failed", "failed", {
71
+ ...detail,
72
+ error: `codegraph init resolved to ancestor database at ${state.ancestorRoot}`,
73
+ projectRoot: context.projectRoot,
74
+ timedOut: false,
75
+ }, context);
76
+ case "uninitialized":
77
+ return finishWorkerFailure("failed", "failed", {
78
+ ...detail,
79
+ error: "codegraph init completed without creating an exact project database",
80
+ projectRoot: context.projectRoot,
81
+ timedOut: false,
82
+ }, context);
83
+ }
84
+ }
85
+
86
+ export function finishWorkerFailure(
87
+ action: WorkerAction,
88
+ failureAction: SessionStartFailureAction,
89
+ detail: Omit<CodegraphSessionStartOutcome, "action">,
90
+ context: WorkerResultContext,
91
+ ): WorkerRunResult {
92
+ let releaseLock = true;
93
+ try {
94
+ recordSessionStartFailure({
95
+ action: failureAction,
96
+ baseCooldownMs: context.baseCooldownMs,
97
+ homeDir: context.homeDir,
98
+ nowMs: context.nowMs(),
99
+ projectRoot: context.projectRoot,
100
+ });
101
+ } catch (error) {
102
+ if (error instanceof Error) releaseLock = false;
103
+ else throw error;
104
+ }
105
+ safeLogWorkerOutcome(context.logOutcome, { ...detail, action });
106
+ return { action, releaseLock };
107
+ }
108
+
109
+ export function finishWorkerSuccess(
110
+ action: WorkerAction,
111
+ detail: Omit<CodegraphSessionStartOutcome, "action">,
112
+ context: WorkerResultContext,
113
+ ): WorkerRunResult {
114
+ try {
115
+ clearSessionStartCooldown({ homeDir: context.homeDir, projectRoot: context.projectRoot });
116
+ } catch (error) {
117
+ if (error instanceof Error) writeDebugLog(`failed to clear SessionStart cooldown: ${error.message}`);
118
+ else throw error;
119
+ }
120
+ safeLogWorkerOutcome(context.logOutcome, { ...detail, action });
121
+ return { action, releaseLock: true };
122
+ }
123
+
124
+ export function finishWorkerNeutral(
125
+ action: WorkerAction,
126
+ detail: Omit<CodegraphSessionStartOutcome, "action">,
127
+ context: WorkerResultContext,
128
+ ): WorkerRunResult {
129
+ safeLogWorkerOutcome(context.logOutcome, { ...detail, action });
130
+ return { action, releaseLock: true };
131
+ }
132
+
133
+ export function safeLogWorkerOutcome(
134
+ logOutcome: (outcome: CodegraphSessionStartOutcome) => void,
135
+ outcome: CodegraphSessionStartOutcome,
136
+ ): void {
137
+ try {
138
+ logOutcome(outcome);
139
+ } catch (error) {
140
+ if (error instanceof Error) processStderr.write(`[codegraph-session-start] failed to write outcome: ${error.message}\n`);
141
+ else throw error;
142
+ }
143
+ }
144
+
145
+ function writeDebugLog(message: string): void {
146
+ if (processEnv["OMO_CODEGRAPH_DEBUG"] !== "1") return;
147
+ processStderr.write(`[codegraph-session-start] ${message}\n`);
148
+ }
@@ -1,45 +1,58 @@
1
- import { execFile } from "node:child_process";
2
- import { appendFileSync, existsSync, mkdirSync } from "node:fs";
3
1
  import { homedir } from "node:os";
4
- import { extname, join } from "node:path";
5
- import { cwd as processCwd, env as processEnv, execPath as processExecPath, stderr as processStderr } from "node:process";
2
+ import { cwd as processCwd, env as processEnv } from "node:process";
6
3
 
7
4
  import { getCodexOmoConfig } from "../../../shared/src/config-loader.ts";
8
- import { buildCodegraphChildEnv, buildCodegraphEnv } from "../../../../../utils/src/codegraph/env.ts";
9
- import { CODEGRAPH_PINNED_VERSION } from "../../../../../utils/src/codegraph/manifest.ts";
5
+ import { hasCodegraphManagedInstall, resolvePinnedCodegraphBin } from "../../../../../utils/src/codegraph/managed-runtime.ts";
10
6
  import { evaluateCodegraphNodeSupport, type CodegraphNodeSupport } from "../../../../../utils/src/codegraph/node-support.ts";
11
7
  import { ensureCodegraphProvisioned } from "../../../../../utils/src/codegraph/provision.ts";
12
- import {
13
- codegraphCommandRequiresSupportedLocalNode,
14
- resolveCodegraphCommand,
15
- type CodegraphCommandResolution,
16
- } from "../../../../../utils/src/codegraph/resolve.ts";
8
+ import { resolveCodegraphCommand } from "../../../../../utils/src/codegraph/resolve.ts";
17
9
  import { ensureCodegraphGitignored, prepareCodegraphWorkspace } from "../../../../../utils/src/codegraph/workspace.ts";
18
10
  import type {
19
- CodegraphCommandResult,
20
- CodegraphConfig,
21
11
  CodegraphSessionStartDeps,
22
12
  CodegraphSessionStartOutcome,
23
13
  SessionStartWorkerOptions,
24
14
  WorkerAction,
25
15
  } from "./hook-types.js";
16
+ import {
17
+ resolveSessionStartCommand,
18
+ runSessionStartCodegraphCommand,
19
+ SESSION_START_COMMAND_TIMEOUT_MS,
20
+ sessionStartCodegraphEnv,
21
+ type CodegraphBootstrapConfig,
22
+ } from "./session-start-command.js";
23
+ import { DEFAULT_SESSION_START_COOLDOWN_MS } from "./session-start-cooldown.js";
24
+ import { releaseSessionStartLock, sessionStartLockIsOwned, type SessionStartLock } from "./session-start-lock.js";
25
+ import { appendSessionStartOutcome } from "./session-start-outcome.js";
26
+ import { resolveSessionStartStatePaths } from "./session-start-paths.js";
27
+ import { probeCodegraphProject, type CodegraphProjectState } from "./session-start-project.js";
28
+ import {
29
+ finishWorkerFailure,
30
+ finishWorkerInitialization,
31
+ finishWorkerNeutral,
32
+ finishWorkerProjectState,
33
+ safeLogWorkerOutcome,
34
+ type WorkerResultContext,
35
+ type WorkerRunResult,
36
+ } from "./session-start-worker-result.js";
26
37
 
27
38
  export const SESSION_START_CWD_ENV = "OMO_CODEGRAPH_SESSION_START_CWD";
39
+ export const SESSION_START_LOCK_TOKEN_ENV = "OMO_CODEGRAPH_SESSION_START_LOCK_TOKEN";
28
40
 
29
- const CODEGRAPH_VERSION = CODEGRAPH_PINNED_VERSION;
30
- const COMMAND_TIMEOUT_MS = 60_000;
31
- const WINDOWS_CMD_EXTENSIONS = new Set([".bat", ".cmd"]);
32
- const WINDOWS_NODE_SCRIPT_EXTENSIONS = new Set([".cjs", ".js", ".mjs"]);
33
- type CodegraphBootstrapConfig = CodegraphConfig & {
34
- readonly trustedCodegraphInstallDir?: string;
41
+ type WorkerContext = WorkerResultContext & {
42
+ readonly config: CodegraphBootstrapConfig;
43
+ readonly deps: CodegraphSessionStartDeps;
44
+ readonly env: Record<string, string | undefined>;
45
+ readonly probeProject: (projectRoot: string) => CodegraphProjectState;
35
46
  };
36
47
 
37
48
  const defaultDeps: CodegraphSessionStartDeps = {
38
49
  ensureGitignored: ensureCodegraphGitignored,
39
50
  ensureProvisioned: ensureCodegraphProvisioned,
51
+ managedInstallExists: hasCodegraphManagedInstall,
40
52
  prepareWorkspace: prepareCodegraphWorkspace,
41
53
  resolveCommand: resolveCodegraphCommand,
42
- runCommand: runCodegraphCommand,
54
+ resolveManagedBin: resolvePinnedCodegraphBin,
55
+ runCommand: runSessionStartCodegraphCommand,
43
56
  };
44
57
 
45
58
  export async function runCodegraphSessionStartWorker(options: SessionStartWorkerOptions = {}): Promise<{ readonly action: WorkerAction }> {
@@ -47,189 +60,116 @@ export async function runCodegraphSessionStartWorker(options: SessionStartWorker
47
60
  const homeDir = resolveHomeDir(env);
48
61
  const projectRoot = options.cwd ?? env[SESSION_START_CWD_ENV] ?? processCwd();
49
62
  const config = options.config ?? getCodexOmoConfig({ cwd: projectRoot, env, homeDir });
50
- const logOutcome = options.logOutcome ?? ((outcome) => appendOutcome(homeDir, outcome));
51
-
52
- if (config.codegraph?.enabled === false) {
53
- return finish("skipped-disabled", { projectRoot }, logOutcome);
63
+ const lock = resolveWorkerLock(homeDir, projectRoot, env[SESSION_START_LOCK_TOKEN_ENV]);
64
+ if (lock !== null && !sessionStartLockIsOwned(lock)) {
65
+ const outcome = { action: "skipped-locked", projectRoot } satisfies CodegraphSessionStartOutcome;
66
+ safeLogWorkerOutcome(options.logOutcome ?? ((value) => appendSessionStartOutcome(homeDir, value)), outcome);
67
+ return { action: "skipped-locked" };
54
68
  }
55
69
 
56
- const nodeSupport = evaluateCodegraphNodeSupport({ env, nodeVersion: options.nodeVersion });
57
- const bootstrapConfig: CodegraphBootstrapConfig = {
58
- ...(config.codegraph ?? {}),
59
- ...(config.trustedCodegraphInstallDir === undefined ? {} : { trustedCodegraphInstallDir: config.trustedCodegraphInstallDir }),
70
+ const customResolutionDeps = options.deps?.resolveCommand === undefined
71
+ ? {}
72
+ : {
73
+ managedInstallExists: options.deps.managedInstallExists ?? (() => false),
74
+ resolveManagedBin: options.deps.resolveManagedBin ?? (() => null),
75
+ };
76
+ const context: WorkerContext = {
77
+ baseCooldownMs: config.codegraph?.session_start_cooldown_ms ?? DEFAULT_SESSION_START_COOLDOWN_MS,
78
+ config: {
79
+ ...(config.codegraph ?? {}),
80
+ ...(config.trustedCodegraphInstallDir === undefined ? {} : { trustedCodegraphInstallDir: config.trustedCodegraphInstallDir }),
81
+ },
82
+ deps: { ...defaultDeps, ...customResolutionDeps, ...options.deps },
83
+ env,
84
+ homeDir,
85
+ logOutcome: options.logOutcome ?? ((outcome) => appendSessionStartOutcome(homeDir, outcome)),
86
+ nowMs: () => options.nowMs ?? Date.now(),
87
+ probeProject: options.projectProbe ?? probeCodegraphProject,
88
+ projectRoot,
60
89
  };
61
- return runBootstrap(projectRoot, bootstrapConfig, env, homeDir, nodeSupport, { ...defaultDeps, ...options.deps }, logOutcome);
90
+
91
+ let result: WorkerRunResult;
92
+ if (config.codegraph?.enabled === false) {
93
+ result = finishWorkerNeutral("skipped-disabled", { projectRoot }, context);
94
+ } else {
95
+ const nodeSupport = evaluateCodegraphNodeSupport({ env, nodeVersion: options.nodeVersion });
96
+ result = await runBootstrap(context, nodeSupport);
97
+ }
98
+ if (lock !== null && result.releaseLock) releaseSessionStartLock(lock);
99
+ return { action: result.action };
62
100
  }
63
101
 
64
- async function runBootstrap(
65
- projectRoot: string,
66
- config: CodegraphBootstrapConfig,
67
- env: Record<string, string | undefined>,
68
- homeDir: string,
69
- nodeSupport: CodegraphNodeSupport,
70
- deps: CodegraphSessionStartDeps,
71
- logOutcome: (outcome: CodegraphSessionStartOutcome) => void,
72
- ): Promise<{ readonly action: WorkerAction }> {
102
+ async function runBootstrap(context: WorkerContext, nodeSupport: CodegraphNodeSupport): Promise<WorkerRunResult> {
73
103
  try {
74
- const command = await resolveOrProvisionCommand(deps, config, env, homeDir, nodeSupport);
104
+ const projectState = context.probeProject(context.projectRoot);
105
+ const projectDecision = finishWorkerProjectState(projectState, context);
106
+ if (projectDecision !== null) return projectDecision;
107
+
108
+ const command = await resolveSessionStartCommand({
109
+ config: context.config,
110
+ deps: context.deps,
111
+ env: context.env,
112
+ homeDir: context.homeDir,
113
+ nodeSupport,
114
+ });
75
115
  if (command.kind === "unavailable") {
76
- return finish("skipped-unavailable", { error: command.error, projectRoot, source: command.source }, logOutcome);
116
+ return finishWorkerFailure("skipped-unavailable", "skipped-unavailable", {
117
+ error: command.error,
118
+ projectRoot: context.projectRoot,
119
+ source: command.source,
120
+ }, context);
77
121
  }
78
122
  if (command.kind === "unsupported-node") {
79
- return finish("skipped-unsupported-node", { projectRoot }, logOutcome);
123
+ return finishWorkerFailure("skipped-unsupported-node", "skipped-unsupported-node", { projectRoot: context.projectRoot }, context);
80
124
  }
81
125
 
82
- deps.prepareWorkspace(projectRoot, { homeDir });
83
- deps.ensureGitignored(projectRoot);
84
- const codegraphEnv = codegraphEnvForConfig(config, homeDir);
85
- const status = await deps.runCommand(projectRoot, command.resolution.command, [...command.resolution.argsPrefix, "status", "--json"], { env: codegraphEnv, timeoutMs: COMMAND_TIMEOUT_MS });
86
- const decision = decideStartupAction(status);
87
- if (decision.kind === "skip") return finish("skipped-status", { error: decision.reason, projectRoot }, logOutcome);
88
-
89
- const actionArgs = command.resolution.argsPrefix.concat(decision.kind === "init" ? ["init"] : ["sync"]);
90
- const action = await deps.runCommand(projectRoot, command.resolution.command, actionArgs, { env: codegraphEnv, timeoutMs: COMMAND_TIMEOUT_MS });
91
- return finish(decision.kind === "init" ? "initialized" : "synced", { exitCode: action.exitCode, projectRoot, source: command.resolution.source, timedOut: action.timedOut }, logOutcome);
92
- } catch (error) {
93
- return finish("failed", { error: error instanceof Error ? error.message : String(error), projectRoot }, logOutcome);
94
- }
95
- }
96
-
97
- function finish(action: WorkerAction, detail: Omit<CodegraphSessionStartOutcome, "action">, logOutcome: (outcome: CodegraphSessionStartOutcome) => void): { readonly action: WorkerAction } {
98
- safeLogOutcome(logOutcome, { ...detail, action });
99
- return { action };
100
- }
101
-
102
- type ResolutionResult =
103
- | { readonly kind: "resolved"; readonly resolution: CodegraphCommandResolution }
104
- | { readonly kind: "unsupported-node" }
105
- | { readonly error: string; readonly kind: "unavailable"; readonly projectRoot?: string; readonly source: CodegraphCommandResolution["source"] };
106
-
107
- async function resolveOrProvisionCommand(
108
- deps: CodegraphSessionStartDeps,
109
- config: CodegraphBootstrapConfig,
110
- env: Record<string, string | undefined>,
111
- homeDir: string,
112
- nodeSupport: CodegraphNodeSupport,
113
- ): Promise<ResolutionResult> {
114
- const trustedInstallDir = config.trustedCodegraphInstallDir;
115
- const resolved = deps.resolveCommand({ env, homeDir, provisioned: () => provisionedBinFromInstallDir(trustedInstallDir) });
116
- if (resolved.exists && canUseResolvedCommand(resolved, nodeSupport)) {
117
- return { kind: "resolved", resolution: resolved };
118
- }
119
- if (resolved.exists && config.auto_provision === false) return { kind: "unsupported-node" };
120
- if (config.auto_provision === false) return { error: "codegraph binary unavailable and auto_provision is disabled", kind: "unavailable", source: resolved.source };
121
-
122
- const installDir = trustedInstallDir ?? join(homeDir, ".omo", "codegraph");
123
- const provisioned = await deps.ensureProvisioned({ installDir, lockDir: join(installDir, ".locks"), version: CODEGRAPH_VERSION });
124
- if (!provisioned.provisioned || provisioned.binPath === undefined) {
125
- return { error: provisioned.error ?? "provisioning did not produce a binary", kind: "unavailable", source: resolved.source };
126
- }
127
- return { kind: "resolved", resolution: { argsPrefix: [], command: provisioned.binPath, exists: true, source: "provisioned" } };
128
- }
129
-
130
- function codegraphEnvForConfig(config: CodegraphBootstrapConfig, homeDir: string): Record<string, string> {
131
- // The detached indexing worker must NEVER engage the upstream daemon, even
132
- // when the user opted into daemon mode for interactive MCP sessions: a
133
- // daemon-backed init/sync would outlive and race the worker's lifecycle.
134
- const env = buildCodegraphEnv({ daemon: false, homeDir });
135
- return config.trustedCodegraphInstallDir === undefined ? env : { ...env, CODEGRAPH_INSTALL_DIR: config.trustedCodegraphInstallDir };
136
- }
137
-
138
- function canUseResolvedCommand(resolved: CodegraphCommandResolution, nodeSupport: CodegraphNodeSupport): boolean {
139
- return !codegraphCommandRequiresSupportedLocalNode(resolved) || nodeSupport.supported;
140
- }
141
-
142
- function decideStartupAction(status: CodegraphCommandResult): { readonly kind: "init" } | { readonly kind: "skip"; readonly reason: string } | { readonly kind: "sync" } {
143
- if (status.timedOut) return { kind: "skip", reason: "status timed out" };
144
- const text = `${status.stdout}\n${status.stderr ?? ""}`.toLowerCase();
145
- if (text.includes("not initialized") || text.includes("uninitialized")) return { kind: "init" };
146
- const initialized = jsonSaysInitialized(parseJson(status.stdout));
147
- if (initialized === false) return { kind: "init" };
148
- if (initialized === true) return { kind: "sync" };
149
- if (status.exitCode !== 0) return { kind: "skip", reason: `status exited ${status.exitCode}` };
150
- return { kind: "sync" };
151
- }
152
-
153
- function jsonSaysInitialized(value: unknown): boolean | undefined {
154
- if (!isRecord(value)) return undefined;
155
- const initialized = value["initialized"] ?? value["isInitialized"] ?? value["ready"];
156
- if (typeof initialized === "boolean") return initialized;
157
- const status = value["status"];
158
- if (typeof status !== "string") return undefined;
159
- const normalized = status.toLowerCase();
160
- if (normalized.includes("not initialized") || normalized.includes("uninitialized")) return false;
161
- if (normalized.includes("initialized") || normalized.includes("ready")) return true;
162
- return undefined;
163
- }
164
-
165
- async function runCodegraphCommand(projectRoot: string, command: string, args: readonly string[], options: { readonly env: Record<string, string>; readonly timeoutMs: number }): Promise<CodegraphCommandResult> {
166
- const invocation = resolveCodegraphCommandInvocation(command, args);
167
- return new Promise((resolvePromise) => {
168
- execFile(invocation.command, [...invocation.args], { cwd: projectRoot, encoding: "utf8", env: buildCodegraphChildEnv({ ambientEnv: processEnv, codegraphEnv: options.env }), maxBuffer: 1024 * 1024, timeout: options.timeoutMs, windowsHide: true }, (error, stdout, stderr) => {
169
- if (error === null) {
170
- resolvePromise({ exitCode: 0, stderr: toOutputText(stderr), stdout: toOutputText(stdout), timedOut: false });
171
- return;
172
- }
173
- resolvePromise({ exitCode: resolveExitCode(error), stderr: toOutputText(stderr), stdout: toOutputText(stdout), timedOut: error.killed === true });
174
- });
175
- });
176
- }
177
-
178
- export function resolveCodegraphCommandInvocation(
179
- command: string,
180
- args: readonly string[],
181
- platform: NodeJS.Platform = process.platform,
182
- ): { readonly args: readonly string[]; readonly command: string } {
183
- if (platform !== "win32") return { args: [...args], command };
184
- const extension = extname(command).toLowerCase();
185
- if (WINDOWS_NODE_SCRIPT_EXTENSIONS.has(extension)) return { args: [command, ...args], command: processExecPath };
186
- if (!WINDOWS_CMD_EXTENSIONS.has(extension)) return { args: [...args], command };
187
- return { args: ["/d", "/s", "/c", command, ...args], command: "cmd.exe" };
188
- }
189
-
190
- function appendOutcome(homeDir: string, outcome: CodegraphSessionStartOutcome): void {
191
- const logDir = join(homeDir, ".omo", "codegraph");
192
- mkdirSync(logDir, { recursive: true });
193
- appendFileSync(join(logDir, "session-start.jsonl"), `${JSON.stringify({ ...outcome, timestamp: new Date().toISOString() })}\n`);
194
- }
195
-
196
- function safeLogOutcome(logOutcome: (outcome: CodegraphSessionStartOutcome) => void, outcome: CodegraphSessionStartOutcome): void {
197
- try {
198
- logOutcome(outcome);
126
+ context.deps.prepareWorkspace(context.projectRoot, { homeDir: context.homeDir });
127
+ context.deps.ensureGitignored(context.projectRoot);
128
+ const preparedState = context.probeProject(context.projectRoot);
129
+ const preparedDecision = finishWorkerProjectState(preparedState, context);
130
+ if (preparedDecision !== null) return preparedDecision;
131
+
132
+ const action = await context.deps.runCommand(
133
+ context.projectRoot,
134
+ command.resolution.command,
135
+ [...command.resolution.argsPrefix, "init"],
136
+ { env: sessionStartCodegraphEnv(context.config, context.homeDir), timeoutMs: SESSION_START_COMMAND_TIMEOUT_MS },
137
+ );
138
+ if (action.timedOut) {
139
+ return finishWorkerFailure("failed", "timeout", {
140
+ error: "codegraph init timed out",
141
+ exitCode: action.exitCode,
142
+ projectRoot: context.projectRoot,
143
+ source: command.resolution.source,
144
+ timedOut: true,
145
+ }, context);
146
+ }
147
+ if (action.exitCode !== 0) {
148
+ return finishWorkerFailure("failed", "failed", {
149
+ error: `codegraph init exited ${action.exitCode}`,
150
+ exitCode: action.exitCode,
151
+ projectRoot: context.projectRoot,
152
+ source: command.resolution.source,
153
+ timedOut: false,
154
+ }, context);
155
+ }
156
+ return finishWorkerInitialization(context.probeProject(context.projectRoot), {
157
+ exitCode: action.exitCode,
158
+ source: command.resolution.source,
159
+ }, context);
199
160
  } catch (error) {
200
- if (error instanceof Error) processStderr.write(`[codegraph-session-start] failed to write outcome: ${error.message}\n`);
201
- else throw error;
161
+ return finishWorkerFailure("failed", "failed", {
162
+ error: error instanceof Error ? error.message : String(error),
163
+ projectRoot: context.projectRoot,
164
+ }, context);
202
165
  }
203
166
  }
204
167
 
205
- function provisionedBinFromInstallDir(installDir: string | undefined): string | null {
206
- if (installDir === undefined) return null;
207
- const candidate = join(installDir, "bin", process.platform === "win32" ? "codegraph.cmd" : "codegraph");
208
- return existsSync(candidate) ? candidate : null;
209
- }
210
-
211
- function resolveExitCode(error: Error): number {
212
- if ("code" in error && typeof error.code === "number") return error.code;
213
- return 1;
214
- }
215
-
216
- function toOutputText(value: string | Buffer): string {
217
- return Buffer.isBuffer(value) ? value.toString("utf8") : value;
168
+ function resolveWorkerLock(homeDir: string, projectRoot: string, token: string | undefined): SessionStartLock | null {
169
+ if (token === undefined || token.length === 0) return null;
170
+ return { path: resolveSessionStartStatePaths(homeDir, projectRoot).lockPath, token };
218
171
  }
219
172
 
220
173
  function resolveHomeDir(env: Record<string, string | undefined>): string {
221
174
  return env["HOME"] ?? env["USERPROFILE"] ?? homedir();
222
175
  }
223
-
224
- function parseJson(text: string): unknown {
225
- try {
226
- return JSON.parse(text);
227
- } catch (error) {
228
- if (error instanceof SyntaxError) return undefined;
229
- throw error;
230
- }
231
- }
232
-
233
- function isRecord(value: unknown): value is Record<string, unknown> {
234
- return typeof value === "object" && value !== null;
235
- }
@@ -20,6 +20,7 @@ describe("CodeGraph SessionStart exclusion policy", () => {
20
20
  // given
21
21
  const stdout: string[] = [];
22
22
  const spawned: WorkerSpawnInvocation[] = [];
23
+ let sweepCalls = 0;
23
24
  const stateRoot = createAllowedWorkspace("codegraph-omo-state");
24
25
  const workspace = join(stateRoot, ".omo", "ulw-research", "run", "clones", "repo");
25
26
  mkdirSync(workspace, { recursive: true });
@@ -33,13 +34,17 @@ describe("CodeGraph SessionStart exclusion policy", () => {
33
34
  stdin: Readable.from(["{}"]),
34
35
  stdout: { write: (chunk) => stdout.push(chunk) },
35
36
  spawnWorker: (invocation) => spawned.push(invocation),
36
- statusProbe: () => {
37
- throw new Error("excluded projects must not probe CodeGraph status");
37
+ ancestorProbe: () => {
38
+ throw new Error("excluded projects must not probe CodeGraph ancestors");
39
+ },
40
+ sweepZombies: () => {
41
+ sweepCalls += 1;
38
42
  },
39
43
  });
40
44
 
41
45
  // then
42
46
  expect(result).toEqual({ action: "skipped-excluded", exitCode: 0 });
47
+ expect(sweepCalls).toBe(0);
43
48
  expect(spawned).toEqual([]);
44
49
  expect(stdout.join("")).toBe("");
45
50
  } finally {
@@ -63,8 +68,8 @@ describe("CodeGraph SessionStart exclusion policy", () => {
63
68
  env: { HOME: "/tmp/home" },
64
69
  stdin: Readable.from(["{}"]),
65
70
  stdout: { write: (chunk) => stdout.push(chunk) },
71
+ ancestorProbe: () => ({ kind: "uninitialized" }),
66
72
  spawnWorker: (invocation) => spawned.push(invocation),
67
- statusProbe: () => Promise.resolve(false),
68
73
  });
69
74
 
70
75
  // then
@@ -92,8 +97,8 @@ describe("CodeGraph SessionStart exclusion policy", () => {
92
97
  env: { HOME: "/tmp/home" },
93
98
  stdin: Readable.from(["{}"]),
94
99
  stdout: { write: (chunk) => stdout.push(chunk) },
100
+ ancestorProbe: () => ({ kind: "uninitialized" }),
95
101
  spawnWorker: (invocation) => spawned.push(invocation),
96
- statusProbe: () => Promise.resolve(false),
97
102
  });
98
103
 
99
104
  // then