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

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 (134) hide show
  1. package/dist/cli/index.js +75 -26
  2. package/dist/cli-node/index.js +75 -26
  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/subagent-spawn-limits.d.ts +8 -0
  9. package/dist/features/task-toast-manager/manager.d.ts +0 -24
  10. package/dist/hooks/anthropic-context-window-limit-recovery/message-storage-directory.d.ts +0 -3
  11. package/dist/hooks/anthropic-context-window-limit-recovery/state.d.ts +1 -0
  12. package/dist/hooks/anthropic-context-window-limit-recovery/storage/empty-messages.d.ts +0 -1
  13. package/dist/hooks/claude-code-hooks/session-hook-state.d.ts +1 -0
  14. package/dist/hooks/claude-code-hooks/stop.d.ts +2 -0
  15. package/dist/hooks/claude-code-hooks/todo.d.ts +0 -1
  16. package/dist/hooks/claude-code-hooks/transcript.d.ts +2 -0
  17. package/dist/hooks/fsync-skip-warning/index.d.ts +4 -0
  18. package/dist/hooks/hashline-edit-diff-enhancer/hook.d.ts +1 -0
  19. package/dist/hooks/hashline-edit-diff-enhancer/pending-captures.d.ts +13 -0
  20. package/dist/hooks/keyword-detector/hook.d.ts +7 -0
  21. package/dist/hooks/keyword-detector/ultrawork/source-detector.d.ts +0 -1
  22. package/dist/hooks/model-fallback/hook.d.ts +1 -1
  23. package/dist/index.js +439 -218
  24. package/dist/oh-my-opencode.schema.json +5 -0
  25. package/dist/plugin-dispose.d.ts +3 -0
  26. package/dist/shared/agent-tool-restrictions.d.ts +0 -1
  27. package/dist/tools/delegate-task/sync-session-cleanup.d.ts +3 -0
  28. package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -0
  29. package/dist/tools/delegate-task/types.d.ts +1 -0
  30. package/dist/tui.js +56 -3
  31. package/package.json +15 -15
  32. package/packages/git-bash-mcp/dist/cli.js +1 -0
  33. package/packages/lsp-core/src/lsp/client-wrapper.test.ts +97 -0
  34. package/packages/lsp-core/src/lsp/client-wrapper.ts +21 -6
  35. package/packages/lsp-core/src/lsp/constants.ts +5 -0
  36. package/packages/lsp-core/src/lsp/errors.ts +15 -0
  37. package/packages/lsp-core/src/lsp/manager-lifecycle.test.ts +262 -0
  38. package/packages/lsp-core/src/lsp/manager.ts +106 -13
  39. package/packages/lsp-core/src/lsp/workspace-markers.ts +18 -1
  40. package/packages/lsp-core/src/mcp.ts +3 -0
  41. package/packages/lsp-daemon/dist/cli.js +114 -18
  42. package/packages/lsp-daemon/dist/client.js +114 -18
  43. package/packages/lsp-daemon/dist/index.js +114 -18
  44. package/packages/lsp-daemon/dist/proxy.js +3 -0
  45. package/packages/lsp-tools-mcp/dist/cli.js +114 -18
  46. package/packages/lsp-tools-mcp/dist/lsp/manager.js +93 -12
  47. package/packages/lsp-tools-mcp/dist/mcp.js +114 -18
  48. package/packages/lsp-tools-mcp/dist/tools.js +113 -18
  49. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  50. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  51. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  52. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +31 -2
  53. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +31 -2
  54. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +12 -0
  56. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable-idle.test.ts +47 -0
  57. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  58. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  59. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  60. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  62. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  63. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  64. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +113 -18
  65. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  66. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  67. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  68. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  69. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  70. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  71. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  72. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  73. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  74. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  75. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
  76. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  77. package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +1 -0
  78. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +2 -0
  79. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +100 -54
  80. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.d.ts +2 -0
  81. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +9 -6
  82. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +4 -2
  83. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +7 -10
  84. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +3 -2
  85. package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.d.ts +15 -0
  86. package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.js +50 -0
  87. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  88. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  89. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +2 -0
  90. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +16 -5
  91. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +11 -14
  92. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +3 -2
  93. package/packages/omo-codex/plugin/components/ulw-loop/src/surface.ts +73 -0
  94. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-final.test.ts +44 -1
  95. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +25 -0
  96. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +7 -3
  97. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-roles.test.ts +45 -2
  98. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +20 -0
  99. package/packages/omo-codex/plugin/components/ulw-loop/test/surface.test.ts +74 -0
  100. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  106. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  107. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  108. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  109. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  110. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  111. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  112. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  113. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  114. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  115. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  116. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  117. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  118. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
  119. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  120. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  121. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  122. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  123. package/packages/omo-codex/plugin/package-lock.json +13 -13
  124. package/packages/omo-codex/plugin/package.json +1 -1
  125. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
  126. package/dist/hooks/ralph-loop/completion-promise-detector-test-input.d.ts +0 -11
  127. package/dist/hooks/ralph-loop/loop-session-recovery.d.ts +0 -7
  128. package/dist/tools/call-omo-agent/message-storage-directory.d.ts +0 -1
  129. package/dist/tools/delegate-task/model-string-parser.d.ts +0 -1
  130. package/packages/lsp-tools-mcp/dist/lsp/cleanup-errors.d.ts +0 -1
  131. package/packages/lsp-tools-mcp/dist/lsp/constants.d.ts +0 -1
  132. package/packages/lsp-tools-mcp/dist/lsp/language-mappings.d.ts +0 -1
  133. package/packages/lsp-tools-mcp/dist/lsp/process-signal-cleanup.d.ts +0 -1
  134. package/packages/lsp-tools-mcp/dist/missing-dependency-result.d.ts +0 -1
@@ -745,6 +745,16 @@ class LspProcessSpawnError extends Error {
745
745
  this.name = "LspProcessSpawnError";
746
746
  }
747
747
  }
748
+
749
+ class LspClientRespawnBudgetExceededError extends Error {
750
+ constructor(serverId, root, retryLimit) {
751
+ super(`LSP server ${serverId} at ${root} failed to stay alive; respawn budget exhausted ` + `(${retryLimit} consecutive dead generations). Retrying after the cooldown may succeed.`);
752
+ this.serverId = serverId;
753
+ this.root = root;
754
+ this.retryLimit = retryLimit;
755
+ this.name = "LspClientRespawnBudgetExceededError";
756
+ }
757
+ }
748
758
  function isLspDeadConnectionError(err) {
749
759
  return err instanceof LspConnectionClosedError || err instanceof LspProcessExitedError;
750
760
  }
@@ -767,6 +777,8 @@ var MAX_RESIDENT_CLIENTS = 6;
767
777
  var REAPER_INTERVAL_MS = 60000;
768
778
  var STOP_HARD_KILL_TIMEOUT_MS = 5000;
769
779
  var STOP_SIGKILL_GRACE_MS = 1000;
780
+ var CLIENT_RESPAWN_RETRY_LIMIT = 2;
781
+ var CLIENT_RESPAWN_COOLDOWN_MS = 60000;
770
782
  var HEADER_SEPARATOR2 = `\r
771
783
  \r
772
784
  `;
@@ -3378,6 +3390,8 @@ function awaitWithSignal2(promise, signal) {
3378
3390
  class LspManager {
3379
3391
  constructor(options = {}) {
3380
3392
  this.clients = new Map;
3393
+ this.pendingStops = new Map;
3394
+ this.respawnBudgets = new Map;
3381
3395
  this.reaperHandle = null;
3382
3396
  this.signalDisposer = null;
3383
3397
  this.disposed = false;
@@ -3403,17 +3417,52 @@ class LspManager {
3403
3417
  getKey(root, serverId) {
3404
3418
  return `${root}::${serverId}`;
3405
3419
  }
3420
+ tombstoneStop(key, client) {
3421
+ const stop = stopClientBestEffort(client);
3422
+ this.pendingStops.set(key, stop);
3423
+ stop.finally(() => {
3424
+ if (this.pendingStops.get(key) === stop) {
3425
+ this.pendingStops.delete(key);
3426
+ }
3427
+ });
3428
+ return stop;
3429
+ }
3430
+ recordDeadGeneration(key) {
3431
+ let budget = this.respawnBudgets.get(key);
3432
+ if (budget === undefined) {
3433
+ budget = { deadGenerations: 0, exhaustedAt: null };
3434
+ this.respawnBudgets.set(key, budget);
3435
+ }
3436
+ budget.deadGenerations += 1;
3437
+ }
3438
+ markHealthyGeneration(key) {
3439
+ this.respawnBudgets.delete(key);
3440
+ }
3441
+ ensureRespawnAllowed(key, root, serverId) {
3442
+ const budget = this.respawnBudgets.get(key);
3443
+ if (budget === undefined || budget.deadGenerations < CLIENT_RESPAWN_RETRY_LIMIT)
3444
+ return;
3445
+ if (budget.exhaustedAt === null) {
3446
+ budget.exhaustedAt = this.now();
3447
+ throw new LspClientRespawnBudgetExceededError(serverId, root, CLIENT_RESPAWN_RETRY_LIMIT);
3448
+ }
3449
+ if (this.now() - budget.exhaustedAt < CLIENT_RESPAWN_COOLDOWN_MS) {
3450
+ throw new LspClientRespawnBudgetExceededError(serverId, root, CLIENT_RESPAWN_RETRY_LIMIT);
3451
+ }
3452
+ budget.deadGenerations = 0;
3453
+ budget.exhaustedAt = null;
3454
+ }
3406
3455
  reapStale() {
3407
3456
  const t = this.now();
3408
3457
  for (const [key, managed] of this.clients) {
3409
3458
  if (managed.isInitializing && managed.initializingSince !== null && t - managed.initializingSince > this.initTimeoutMs) {
3410
- stopClientBestEffort(managed.client);
3411
3459
  this.clients.delete(key);
3460
+ this.tombstoneStop(key, managed.client);
3412
3461
  continue;
3413
3462
  }
3414
3463
  if (!managed.isInitializing && managed.refCount === 0 && managed.pendingWaiters === 0 && t - managed.lastUsedAt > this.idleTimeoutMs) {
3415
- stopClientBestEffort(managed.client);
3416
3464
  this.clients.delete(key);
3465
+ this.tombstoneStop(key, managed.client);
3417
3466
  }
3418
3467
  }
3419
3468
  }
@@ -3423,7 +3472,7 @@ class LspManager {
3423
3472
  if (!victim)
3424
3473
  return;
3425
3474
  this.clients.delete(victim.key);
3426
- stopClientBestEffort(victim.managed.client);
3475
+ this.tombstoneStop(victim.key, victim.managed.client);
3427
3476
  }
3428
3477
  }
3429
3478
  leastRecentlyUsedIdleClient() {
@@ -3440,7 +3489,7 @@ class LspManager {
3440
3489
  async tryDeleteIfOrphaned(key, managed) {
3441
3490
  if (managed.refCount === 0 && managed.pendingWaiters === 0 && !managed.isInitializing && this.clients.get(key) === managed) {
3442
3491
  this.clients.delete(key);
3443
- await stopClientBestEffort(managed.client);
3492
+ await this.tombstoneStop(key, managed.client);
3444
3493
  }
3445
3494
  }
3446
3495
  async getClient(root, server2, signal) {
@@ -3449,12 +3498,22 @@ class LspManager {
3449
3498
  }
3450
3499
  signal?.throwIfAborted();
3451
3500
  const key = this.getKey(root, server2.id);
3501
+ for (;; ) {
3502
+ const pendingStop = this.pendingStops.get(key);
3503
+ if (pendingStop === undefined)
3504
+ break;
3505
+ await awaitWithSignal2(pendingStop, signal);
3506
+ signal?.throwIfAborted();
3507
+ }
3508
+ if (this.disposed) {
3509
+ throw new Error("LspManager has been disposed");
3510
+ }
3452
3511
  let managed = this.clients.get(key);
3453
3512
  if (managed) {
3454
3513
  const t = this.now();
3455
3514
  if (managed.isInitializing && managed.initializingSince !== null && t - managed.initializingSince > this.initTimeoutMs) {
3456
- await stopClientBestEffort(managed.client);
3457
3515
  this.clients.delete(key);
3516
+ await this.tombstoneStop(key, managed.client);
3458
3517
  managed = undefined;
3459
3518
  }
3460
3519
  }
@@ -3475,15 +3534,20 @@ class LspManager {
3475
3534
  signal.throwIfAborted();
3476
3535
  }
3477
3536
  if (!managed.client.isAlive()) {
3478
- await stopClientBestEffort(managed.client);
3479
- this.clients.delete(key);
3537
+ this.recordDeadGeneration(key);
3538
+ if (this.clients.get(key) === managed) {
3539
+ this.clients.delete(key);
3540
+ }
3541
+ await this.tombstoneStop(key, managed.client);
3480
3542
  return this.getClient(root, server2, signal);
3481
3543
  }
3544
+ this.markHealthyGeneration(key);
3482
3545
  managed.refCount++;
3483
3546
  managed.lastUsedAt = this.now();
3484
3547
  return managed.client;
3485
3548
  }
3486
3549
  this.evictForAdmission();
3550
+ this.ensureRespawnAllowed(key, root, server2.id);
3487
3551
  const client = this.clientFactory(root, server2);
3488
3552
  const initStartedAt = this.now();
3489
3553
  const initPromise = (async () => {
@@ -3507,7 +3571,7 @@ class LspManager {
3507
3571
  if (this.clients.get(key) === newManaged) {
3508
3572
  this.clients.delete(key);
3509
3573
  }
3510
- await stopClientBestEffort(client);
3574
+ await this.tombstoneStop(key, client);
3511
3575
  throw err;
3512
3576
  }
3513
3577
  newManaged.pendingWaiters--;
@@ -3518,6 +3582,15 @@ class LspManager {
3518
3582
  await this.tryDeleteIfOrphaned(key, newManaged);
3519
3583
  signal.throwIfAborted();
3520
3584
  }
3585
+ if (!client.isAlive()) {
3586
+ this.recordDeadGeneration(key);
3587
+ if (this.clients.get(key) === newManaged) {
3588
+ this.clients.delete(key);
3589
+ }
3590
+ await this.tombstoneStop(key, client);
3591
+ return this.getClient(root, server2, signal);
3592
+ }
3593
+ this.markHealthyGeneration(key);
3521
3594
  newManaged.refCount++;
3522
3595
  newManaged.lastUsedAt = this.now();
3523
3596
  return client;
@@ -3538,15 +3611,20 @@ class LspManager {
3538
3611
  if (client && managed.client !== client)
3539
3612
  return;
3540
3613
  this.clients.delete(key);
3541
- stopClientBestEffort(managed.client);
3614
+ this.tombstoneStop(key, managed.client);
3542
3615
  }
3543
3616
  warmupClient(root, server2) {
3544
3617
  if (this.disposed)
3545
3618
  return;
3546
3619
  const key = this.getKey(root, server2.id);
3547
- if (this.clients.has(key))
3620
+ if (this.clients.has(key) || this.pendingStops.has(key))
3548
3621
  return;
3549
3622
  this.evictForAdmission();
3623
+ try {
3624
+ this.ensureRespawnAllowed(key, root, server2.id);
3625
+ } catch {
3626
+ return;
3627
+ }
3550
3628
  const client = this.clientFactory(root, server2);
3551
3629
  const initStartedAt = this.now();
3552
3630
  const initPromise = (async () => {
@@ -3572,7 +3650,7 @@ class LspManager {
3572
3650
  if (this.clients.get(key) === managed) {
3573
3651
  this.clients.delete(key);
3574
3652
  }
3575
- stopClientBestEffort(client);
3653
+ this.tombstoneStop(key, client);
3576
3654
  });
3577
3655
  }
3578
3656
  isServerInitializing(root, serverId) {
@@ -3616,8 +3694,11 @@ class LspManager {
3616
3694
  for (const managed of this.clients.values()) {
3617
3695
  stopPromises.push(stopClientBestEffort(managed.client));
3618
3696
  }
3697
+ const tombstonedStops = [...this.pendingStops.values()];
3619
3698
  this.clients.clear();
3620
- await Promise.allSettled(stopPromises);
3699
+ this.respawnBudgets.clear();
3700
+ await Promise.allSettled([...stopPromises, ...tombstonedStops]);
3701
+ this.pendingStops.clear();
3621
3702
  }
3622
3703
  }
3623
3704
  var _defaultInstance = null;
@@ -3627,7 +3708,16 @@ function getLspManager() {
3627
3708
  }
3628
3709
  return _defaultInstance;
3629
3710
  }
3630
- var WORKSPACE_MARKERS = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
3711
+ var GIT_WORKSPACE_MARKER = ".git";
3712
+ var PROJECT_WORKSPACE_MARKERS = [
3713
+ "package.json",
3714
+ "pyproject.toml",
3715
+ "Cargo.toml",
3716
+ "go.mod",
3717
+ "pom.xml",
3718
+ "build.gradle"
3719
+ ];
3720
+ var WORKSPACE_MARKERS = [GIT_WORKSPACE_MARKER, ...PROJECT_WORKSPACE_MARKERS];
3631
3721
  function findWorkspaceRootOutsideContext(directory) {
3632
3722
  const marked = nearestMarkedAncestor(directory);
3633
3723
  if (marked !== undefined)
@@ -4297,20 +4387,25 @@ function findWorkspaceRoot(filePath) {
4297
4387
  if (!isPathInside(cwd, abs))
4298
4388
  return findWorkspaceRootOutsideContext(dir);
4299
4389
  const fallbackRoot = nearestExistingDirectoryInsideContext(dir, cwd) ?? cwd;
4390
+ let nearestPackageRoot;
4300
4391
  while (isPathInside(cwd, dir)) {
4301
4392
  const canonicalDir = existingDirectoryInsideContext(dir, cwd);
4302
4393
  if (canonicalDir !== undefined) {
4303
- for (const marker of WORKSPACE_MARKERS) {
4304
- if (existsSync11(join5(dir, marker))) {
4305
- return canonicalDir;
4306
- }
4394
+ if (existsSync11(join5(dir, GIT_WORKSPACE_MARKER))) {
4395
+ return canonicalDir;
4396
+ }
4397
+ if (nearestPackageRoot === undefined && hasProjectWorkspaceMarker(dir)) {
4398
+ nearestPackageRoot = canonicalDir;
4307
4399
  }
4308
4400
  }
4309
4401
  if (dir === cwd)
4310
4402
  break;
4311
4403
  dir = dirname10(dir);
4312
4404
  }
4313
- return fallbackRoot;
4405
+ return nearestPackageRoot ?? fallbackRoot;
4406
+ }
4407
+ function hasProjectWorkspaceMarker(directory) {
4408
+ return PROJECT_WORKSPACE_MARKERS.some((marker) => existsSync11(join5(directory, marker)));
4314
4409
  }
4315
4410
  function resolveReadablePathInsideContext(filePath) {
4316
4411
  const cwd = contextCwd();
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 60,
11
- "statusMessage": "(OmO 5.0.0-beta.30) Checking LSP Diagnostics"
11
+ "statusMessage": "(OmO 5.0.0-beta.31) Checking LSP Diagnostics"
12
12
  }
13
13
  ]
14
14
  }
@@ -21,7 +21,7 @@
21
21
  "type": "command",
22
22
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
23
23
  "timeout": 5,
24
- "statusMessage": "(OmO 5.0.0-beta.30) Resetting LSP Diagnostics Cache"
24
+ "statusMessage": "(OmO 5.0.0-beta.31) Resetting LSP Diagnostics Cache"
25
25
  }
26
26
  ]
27
27
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-lsp",
3
- "version": "5.0.0-beta.30",
3
+ "version": "5.0.0-beta.31",
4
4
  "description": "Codex plugin that exposes Language Server Protocol tools and post-edit diagnostics.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.30) Loading Project Rules"
10
+ "statusMessage": "(OmO 5.0.0-beta.31) Loading Project Rules"
11
11
  }
12
12
  ]
13
13
  }
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
21
21
  "timeout": 10,
22
- "statusMessage": "(OmO 5.0.0-beta.30) Loading Project Rules"
22
+ "statusMessage": "(OmO 5.0.0-beta.31) Loading Project Rules"
23
23
  }
24
24
  ]
25
25
  }
@@ -32,7 +32,7 @@
32
32
  "type": "command",
33
33
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
34
34
  "timeout": 10,
35
- "statusMessage": "(OmO 5.0.0-beta.30) Matching Project Rules"
35
+ "statusMessage": "(OmO 5.0.0-beta.31) Matching Project Rules"
36
36
  }
37
37
  ]
38
38
  }
@@ -45,7 +45,7 @@
45
45
  "type": "command",
46
46
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
47
47
  "timeout": 10,
48
- "statusMessage": "(OmO 5.0.0-beta.30) Resetting Project Rule Cache"
48
+ "statusMessage": "(OmO 5.0.0-beta.31) Resetting Project Rule Cache"
49
49
  }
50
50
  ]
51
51
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-rules",
3
- "version": "5.0.0-beta.30",
3
+ "version": "5.0.0-beta.31",
4
4
  "description": "Codex plugin that injects project rule files into model context through lifecycle hooks.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 10,
11
- "statusMessage": "(OmO 5.0.0-beta.30) Checking Thread Title Hygiene"
11
+ "statusMessage": "(OmO 5.0.0-beta.31) Checking Thread Title Hygiene"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-teammode",
3
- "version": "5.0.0-beta.30",
3
+ "version": "5.0.0-beta.31",
4
4
  "description": "Codex team-mode hook component that keeps background thread titles descriptive after create_thread.",
5
5
  "type": "module",
6
6
  "private": true,
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.0.0-beta.30) Recording Session Telemetry"
10
+ "statusMessage": "(OmO 5.0.0-beta.31) Recording Session Telemetry"
11
11
  }
12
12
  ]
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-telemetry",
3
- "version": "5.0.0-beta.30",
3
+ "version": "5.0.0-beta.31",
4
4
  "description": "Codex plugin component that emits omo-codex anonymous daily-active telemetry on SessionStart.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.0.0-beta.30) Checking Ultrawork Trigger"
10
+ "statusMessage": "(OmO 5.0.0-beta.31) Checking Ultrawork Trigger"
11
11
  }
12
12
  ]
13
13
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-ultrawork",
3
- "version": "5.0.0-beta.30",
3
+ "version": "5.0.0-beta.31",
4
4
  "description": "Codex plugin that injects the ultrawork orchestration directive and ships LazyCodex planning, review, QA, and gate agent roles.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ulw-execute-continuation/dist/cli.js\" hook stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.30) Checking Ulw-Execute Continuation"
10
+ "statusMessage": "(OmO 5.0.0-beta.31) Checking Ulw-Execute Continuation"
11
11
  }
12
12
  ]
13
13
  }
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "node \"${PLUGIN_ROOT}/components/ulw-execute-continuation/dist/cli.js\" hook subagent-stop",
21
21
  "timeout": 10,
22
- "statusMessage": "(OmO 5.0.0-beta.30) Checking Ulw-Execute Continuation"
22
+ "statusMessage": "(OmO 5.0.0-beta.31) Checking Ulw-Execute Continuation"
23
23
  }
24
24
  ]
25
25
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-ulw-execute-continuation",
3
- "version": "5.0.0-beta.30",
3
+ "version": "5.0.0-beta.31",
4
4
  "description": "Codex Stop hook continuation injector for omo-codex ulw-execute plans.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -50,6 +50,7 @@ Conventions for human contributors and AI agents working on this repository.
50
50
  - `src/quality-gate.ts` (266 LOC), `src/checkpoint.ts` (260), `src/steering.ts` (227): state-transition hotspots (evidence containment, checkpoint reconciliation, steering mutations).
51
51
  - `src/codex-hook.ts`: UserPromptSubmit steering injection + `create_goal` budget guard.
52
52
  - `src/spawn-guard.ts`, `src/stop-resume-hook.ts`: spawn guards, Stop auto-resume.
53
+ - `src/surface.ts`: toolkit surface + reviewer-identity resolution (`lazycodex` default; staged `surface.json` marker or `OMO_AGENT_TOOLKIT_SURFACE` selects `omo-senpi`).
53
54
  - `src/ultrawork-skill-pointer.ts`: byte-identical mirror of ultrawork's pointer (pinned by `plugin/test/ultrawork-skill-pointer.test.mjs`).
54
55
  - `directive.md`: runtime-read directive (never inlined into TypeScript).
55
56
 
@@ -9,6 +9,7 @@ import { codexGoalMode, compatibleCodexObjectives, expectedCodexObjective, isFin
9
9
  import { ulwLoopAttemptEvidenceDir } from "./paths.js";
10
10
  import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
11
11
  import { classifyExternalAuthorizationBlocker, clearGoalBlockerFields, sameBlockerOccurrences, validateQualityGate, } from "./quality-gate.js";
12
+ import { resolveToolkitSurface } from "./surface.js";
12
13
  import { iso, UlwLoopError } from "./types.js";
13
14
  import { batchClosedBy, requireAllValidationBatchesClosed, requireBatchFinalReady, requireBatchGate } from "./validation-batch.js";
14
15
  const QUALITY_GATE_FS = { existsSync, statSync };
@@ -158,6 +159,7 @@ export async function checkpointUlwLoop(repoRoot, args, scope) {
158
159
  qualityGate = validateQualityGate(await readJsonInput(args.qualityGateJson, repoRoot), {
159
160
  repoRoot,
160
161
  fs: QUALITY_GATE_FS,
162
+ reviewerSurface: resolveToolkitSurface(),
161
163
  ...(plan.evidenceLayoutVersion === 2
162
164
  ? { currentAttemptDir: ulwLoopAttemptEvidenceDir(goal.id, goal.attempt, scope) }
163
165
  : {}),