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
@@ -9233,6 +9233,11 @@
9233
9233
  "minimum": 1,
9234
9234
  "maximum": 9007199254740991
9235
9235
  },
9236
+ "maxLiveDescendantsPerRoot": {
9237
+ "type": "integer",
9238
+ "minimum": 0,
9239
+ "maximum": 9007199254740991
9240
+ },
9236
9241
  "staleTimeoutMs": {
9237
9242
  "type": "number",
9238
9243
  "minimum": 60000
@@ -6,5 +6,8 @@ export declare function createPluginDispose(args: {
6
6
  skillMcpManager: {
7
7
  disconnectAll: () => Promise<void>;
8
8
  };
9
+ tuiStateMirror?: {
10
+ stop: () => void;
11
+ };
9
12
  disposeHooks: () => void;
10
13
  }): PluginDispose;
@@ -2,5 +2,4 @@ type AgentToolRestrictionsOptions = {
2
2
  includeTeamToolDenylist?: boolean;
3
3
  };
4
4
  export declare function getAgentToolRestrictions(agentName: string, options?: AgentToolRestrictionsOptions): Record<string, boolean>;
5
- export declare function hasAgentToolRestrictions(agentName: string): boolean;
6
5
  export {};
@@ -0,0 +1,3 @@
1
+ import type { OpencodeClient } from "./types";
2
+ export declare function cancelSyncSessionDeletion(sessionID: string): void;
3
+ export declare function scheduleSyncSessionDeletion(client: OpencodeClient, sessionID: string, delayMs?: number): void;
@@ -8,6 +8,7 @@ export declare function pollSyncSession(ctx: ToolContextWithMetadata, client: Op
8
8
  } | null | undefined;
9
9
  taskId: string | undefined;
10
10
  anchorMessageCount?: number;
11
+ anchorMessageID?: string;
11
12
  maxAssistantTurns?: number;
12
13
  hasActiveChildBackgroundTasks?: (sessionID: string) => boolean;
13
14
  hasPendingParentWake?: (sessionID: string) => boolean;
@@ -46,6 +46,7 @@ export interface OmoAgentClient {
46
46
  };
47
47
  readonly session: {
48
48
  readonly abort: (input: SessionPathInput) => Promise<unknown>;
49
+ readonly delete?: (input: SessionPathInput) => Promise<unknown>;
49
50
  readonly create: (input: {
50
51
  readonly body: Record<string, unknown>;
51
52
  readonly query?: {
package/dist/tui.js CHANGED
@@ -17156,7 +17156,7 @@ var init_git_master = __esm(() => {
17156
17156
  });
17157
17157
 
17158
17158
  // packages/omo-config-core/src/schema/memory.ts
17159
- var OmoMemoryReflectionTriggerSchema, OmoMemoryReflectionSchema, OmoMemorySyncSchema, OmoMemorySearchSchema, OmoMemoryNudgeSchema, OmoMemoryFactsSchema, OmoMemoryDreamSchema, OmoMemoryPeopleSchema, OmoMemorySoulSchema, OmoMemoryWriteNoticeSchema, OmoMemoryReflectionTriggerLayerSchema, OmoMemoryReflectionLayerSchema, OmoMemorySyncLayerSchema, OmoMemorySearchLayerSchema, OmoMemoryNudgeLayerSchema, OmoMemoryFactsLayerSchema, OmoMemoryDreamLayerSchema, OmoMemoryPeopleLayerSchema, OmoMemorySoulLayerSchema, OmoMemoryWriteNoticeLayerSchema, OmoMemoryAgentOverridesSchema, OmoMemorySettingsSchema, OmoMemorySettingsLayerSchema;
17159
+ var OmoMemoryReflectionTriggerSchema, OmoMemoryReflectionSchema, OmoMemorySyncSchema, OmoMemorySearchSchema, OmoMemoryRecallSchema, OmoMemoryNudgeSchema, OmoMemoryFactsSchema, OmoMemoryDreamSchema, OmoMemoryPeopleSchema, OmoMemorySoulSchema, OmoMemoryWriteNoticeSchema, OmoMemoryReflectionTriggerLayerSchema, OmoMemoryReflectionLayerSchema, OmoMemorySyncLayerSchema, OmoMemorySearchLayerSchema, OmoMemoryRecallLayerSchema, OmoMemoryNudgeLayerSchema, OmoMemoryFactsLayerSchema, OmoMemoryDreamLayerSchema, OmoMemoryPeopleLayerSchema, OmoMemorySoulLayerSchema, OmoMemoryWriteNoticeLayerSchema, OmoMemoryAgentOverridesSchema, OmoMemorySettingsSchema, OmoMemorySettingsLayerSchema;
17160
17160
  var init_memory = __esm(() => {
17161
17161
  init_zod();
17162
17162
  OmoMemoryReflectionTriggerSchema = object({
@@ -17178,6 +17178,14 @@ var init_memory = __esm(() => {
17178
17178
  OmoMemorySearchSchema = object({
17179
17179
  enabled: boolean2().default(true)
17180
17180
  }).strict();
17181
+ OmoMemoryRecallSchema = object({
17182
+ enabled: boolean2().default(true),
17183
+ max_items: number2().int().min(1).max(5).default(2),
17184
+ budget_tokens: number2().int().positive().default(600),
17185
+ excerpt_chars: number2().int().positive().default(200),
17186
+ min_score: number2().optional(),
17187
+ exclude: array(string2()).default([])
17188
+ }).strict();
17181
17189
  OmoMemoryNudgeSchema = object({
17182
17190
  enabled: boolean2().default(true),
17183
17191
  every_user_turns: number2().int().min(1).default(10)
@@ -17224,6 +17232,14 @@ var init_memory = __esm(() => {
17224
17232
  OmoMemorySearchLayerSchema = object({
17225
17233
  enabled: boolean2().optional()
17226
17234
  }).strict();
17235
+ OmoMemoryRecallLayerSchema = object({
17236
+ enabled: boolean2().optional(),
17237
+ max_items: number2().int().min(1).max(5).optional(),
17238
+ budget_tokens: number2().int().positive().optional(),
17239
+ excerpt_chars: number2().int().positive().optional(),
17240
+ min_score: number2().optional(),
17241
+ exclude: array(string2()).optional()
17242
+ }).strict();
17227
17243
  OmoMemoryNudgeLayerSchema = object({
17228
17244
  enabled: boolean2().optional(),
17229
17245
  every_user_turns: number2().int().min(1).optional()
@@ -17263,6 +17279,7 @@ var init_memory = __esm(() => {
17263
17279
  write_notice: OmoMemoryWriteNoticeLayerSchema.optional(),
17264
17280
  sync: OmoMemorySyncLayerSchema.optional(),
17265
17281
  search: OmoMemorySearchLayerSchema.optional(),
17282
+ recall: OmoMemoryRecallLayerSchema.optional(),
17266
17283
  compile_warn_tokens: number2().int().positive().optional()
17267
17284
  }).strict();
17268
17285
  OmoMemorySettingsSchema = object({
@@ -17292,6 +17309,13 @@ var init_memory = __esm(() => {
17292
17309
  write_notice: OmoMemoryWriteNoticeSchema.default({ enabled: true }),
17293
17310
  sync: OmoMemorySyncSchema.default({ enabled: true }),
17294
17311
  search: OmoMemorySearchSchema.default({ enabled: true }),
17312
+ recall: OmoMemoryRecallSchema.default({
17313
+ enabled: true,
17314
+ max_items: 2,
17315
+ budget_tokens: 600,
17316
+ excerpt_chars: 200,
17317
+ exclude: []
17318
+ }),
17295
17319
  compile_warn_tokens: number2().int().positive().default(30000),
17296
17320
  agents: record(string2(), OmoMemoryAgentOverridesSchema).default({})
17297
17321
  }).strict();
@@ -17308,6 +17332,7 @@ var init_memory = __esm(() => {
17308
17332
  write_notice: OmoMemoryWriteNoticeLayerSchema.optional(),
17309
17333
  sync: OmoMemorySyncLayerSchema.optional(),
17310
17334
  search: OmoMemorySearchLayerSchema.optional(),
17335
+ recall: OmoMemoryRecallLayerSchema.optional(),
17311
17336
  compile_warn_tokens: number2().int().positive().optional(),
17312
17337
  agents: record(string2(), OmoMemoryAgentOverridesSchema).optional()
17313
17338
  }).strict();
@@ -17341,11 +17366,11 @@ function resolveOmoTaskSettings(input, resolveParallelism = availableParallelism
17341
17366
  const record2 = record(string2(), unknown()).parse(input);
17342
17367
  return OmoTaskSettingsSchema.parse({
17343
17368
  ...record2,
17344
- residency_max_children: record2["residency_max_children"] ?? Math.max(8, resolveParallelism() * 3),
17369
+ residency_max_children: record2["residency_max_children"] ?? Math.min(DEFAULT_RESIDENCY_MAX_CHILDREN, Math.max(8, resolveParallelism() * 2)),
17345
17370
  global_concurrency: record2["global_concurrency"] ?? Math.max(8, resolveParallelism() * 2)
17346
17371
  });
17347
17372
  }
17348
- var ResidencyMaxChildrenInputSchema, OmoTaskWaitSchema, OmoTaskTeamSettingsSchema, OmoTaskWarningsSchema, OmoTaskDagSettingsSchema, OmoTaskSettingsSchema, OmoTaskDagSettingsLayerSchema, OmoTaskWaitLayerSchema, OmoTaskTeamSettingsLayerSchema, OmoTaskWarningsLayerSchema, OmoTaskSettingsLayerSchema;
17373
+ var DEFAULT_RESIDENCY_MAX_CHILDREN = 16, ResidencyMaxChildrenInputSchema, OmoTaskWaitSchema, OmoTaskTeamSettingsSchema, OmoTaskWarningsSchema, OmoTaskDagSettingsSchema, OmoTaskSettingsSchema, OmoTaskDagSettingsLayerSchema, OmoTaskWaitLayerSchema, OmoTaskTeamSettingsLayerSchema, OmoTaskWarningsLayerSchema, OmoTaskSettingsLayerSchema;
17349
17374
  var init_task = __esm(() => {
17350
17375
  init_zod();
17351
17376
  ResidencyMaxChildrenInputSchema = union([number2().int().nonnegative(), literal("unlimited")]);
@@ -19367,6 +19392,33 @@ var init_agent_model_requirements = __esm(() => {
19367
19392
  { providers: ["opencode-go"], model: "glm-5.2" }
19368
19393
  ]
19369
19394
  },
19395
+ "omo-senpi-code-reviewer": {
19396
+ fallbackChain: [
19397
+ { providers: ["openai", "openai-codex"], model: "gpt-5.6-terra", variant: "medium" },
19398
+ { providers: ["github-copilot"], model: "gpt-5.6-terra", variant: "medium" },
19399
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
19400
+ { providers: ["opencode-go"], model: "glm-5.2" },
19401
+ { providers: ["kimi-for-coding"], model: "kimi-k3" }
19402
+ ]
19403
+ },
19404
+ "omo-senpi-qa-executor": {
19405
+ fallbackChain: [
19406
+ { providers: ["openai", "openai-codex"], model: "gpt-5.6-luna", variant: "high" },
19407
+ { providers: ["github-copilot"], model: "gpt-5.6-luna", variant: "high" },
19408
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
19409
+ { providers: ["opencode-go"], model: "glm-5.2" },
19410
+ { providers: ["kimi-for-coding"], model: "kimi-k3" }
19411
+ ]
19412
+ },
19413
+ "omo-senpi-gate-reviewer": {
19414
+ fallbackChain: [
19415
+ { providers: ["openai", "openai-codex"], model: "gpt-5.6-sol", variant: "low" },
19416
+ { providers: ["github-copilot"], model: "gpt-5.6-sol", variant: "low" },
19417
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-5", variant: "max" },
19418
+ { providers: ["opencode-go"], model: "glm-5.2" },
19419
+ { providers: ["kimi-for-coding"], model: "kimi-k3" }
19420
+ ]
19421
+ },
19370
19422
  atlas: {
19371
19423
  fallbackChain: [
19372
19424
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-5" },
@@ -79135,6 +79187,7 @@ var init_background_task = __esm(() => {
79135
79187
  providerConcurrency: exports_external.record(exports_external.string(), exports_external.number().min(0)).optional(),
79136
79188
  modelConcurrency: exports_external.record(exports_external.string(), exports_external.number().min(0)).optional(),
79137
79189
  maxDepth: exports_external.number().int().min(1).optional(),
79190
+ maxLiveDescendantsPerRoot: exports_external.number().int().min(0).optional(),
79138
79191
  staleTimeoutMs: exports_external.number().min(60000).optional(),
79139
79192
  messageStalenessTimeoutMs: exports_external.number().min(60000).optional(),
79140
79193
  taskTtlMs: exports_external.number().min(300000).optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode",
3
- "version": "5.0.0-beta.30",
3
+ "version": "5.0.0-beta.31",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
5
  "main": "./dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -181,7 +181,7 @@
181
181
  "zod": "^4.4.3"
182
182
  },
183
183
  "devDependencies": {
184
- "@code-yeongyu/senpi": "2026.8.30-3",
184
+ "@code-yeongyu/senpi": "2026.8.31",
185
185
  "@oh-my-opencode/agents-md-core": "workspace:*",
186
186
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
187
187
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -222,18 +222,18 @@
222
222
  "typescript": "^7.0.2"
223
223
  },
224
224
  "optionalDependencies": {
225
- "oh-my-opencode-darwin-arm64": "5.0.0-beta.30",
226
- "oh-my-opencode-darwin-x64": "5.0.0-beta.30",
227
- "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.30",
228
- "oh-my-opencode-linux-arm64": "5.0.0-beta.30",
229
- "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.30",
230
- "oh-my-opencode-linux-x64": "5.0.0-beta.30",
231
- "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.30",
232
- "oh-my-opencode-linux-x64-musl": "5.0.0-beta.30",
233
- "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.30",
234
- "oh-my-opencode-windows-arm64": "5.0.0-beta.30",
235
- "oh-my-opencode-windows-x64": "5.0.0-beta.30",
236
- "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.30"
225
+ "oh-my-opencode-darwin-arm64": "5.0.0-beta.31",
226
+ "oh-my-opencode-darwin-x64": "5.0.0-beta.31",
227
+ "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.31",
228
+ "oh-my-opencode-linux-arm64": "5.0.0-beta.31",
229
+ "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.31",
230
+ "oh-my-opencode-linux-x64": "5.0.0-beta.31",
231
+ "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.31",
232
+ "oh-my-opencode-linux-x64-musl": "5.0.0-beta.31",
233
+ "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.31",
234
+ "oh-my-opencode-windows-arm64": "5.0.0-beta.31",
235
+ "oh-my-opencode-windows-x64": "5.0.0-beta.31",
236
+ "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.31"
237
237
  },
238
238
  "overrides": {
239
239
  "@earendil-works/pi-agent-core": "0.84.2",
@@ -251,6 +251,6 @@
251
251
  "@code-yeongyu/comment-checker"
252
252
  ],
253
253
  "patchedDependencies": {
254
- "@code-yeongyu/senpi@2026.8.30-3": "patches/@code-yeongyu%2Fsenpi@2026.8.30-3.patch"
254
+ "@code-yeongyu/senpi@2026.8.31": "patches/@code-yeongyu%2Fsenpi@2026.8.31.patch"
255
255
  }
256
256
  }
@@ -180,6 +180,7 @@ async function runJsonRpcStdioServer(config) {
180
180
  const idleTimer = createIdleTimer(idleTimeoutMs, log, () => {
181
181
  isClosed = true;
182
182
  config.onIdleTimeout?.();
183
+ config.input.destroy();
183
184
  });
184
185
  const watchdog = createParentWatchdog(config.parentWatchdog, (parentPid, pollIntervalMs) => {
185
186
  isClosed = true;
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { afterEach, describe, expect, it } from "bun:test";
5
5
 
6
6
  import { createStandaloneMcpRequestContext, runWithRequestContext } from "../request-context.js";
7
+ import type { LspClient } from "./client.js";
7
8
  import { findWorkspaceRoot, resolveReadablePathInsideContext, withLspClient } from "./client-wrapper.js";
8
9
  import { LspInvalidPathError } from "./errors.js";
9
10
  import { LspManager } from "./manager.js";
@@ -22,6 +23,102 @@ function tempRoot(prefix: string): string {
22
23
  return root;
23
24
  }
24
25
 
26
+ describe("LSP workspace root collapse in monorepos", () => {
27
+ it("#given a git repository containing four package.json packages #when resolving workspace roots for files in each package #then all collapse to the repository root", () => {
28
+ const root = tempRoot("lsp-client-wrapper-monorepo-");
29
+ mkdirSync(join(root, ".git"), { recursive: true });
30
+ const packageDirs = ["alpha", "beta", "gamma", "delta"].map((name) => join(root, "packages", name));
31
+ for (const packageDir of packageDirs) {
32
+ mkdirSync(join(packageDir, "src"), { recursive: true });
33
+ writeFileSync(join(packageDir, "package.json"), "{}\n");
34
+ writeFileSync(join(packageDir, "src", "file.ts"), "export const value = 1;\n");
35
+ }
36
+ const context = createStandaloneMcpRequestContext({ cwd: root });
37
+
38
+ const roots = packageDirs.map((packageDir) =>
39
+ runWithRequestContext(context, () => findWorkspaceRoot(join(packageDir, "src", "file.ts"))),
40
+ );
41
+
42
+ expect(new Set(roots).size).toBe(1);
43
+ expect(roots[0]).toBe(realpathSync(root));
44
+ });
45
+
46
+ it("#given nested package.json markers without any .git #when resolving workspace #then keeps the nearest package root", () => {
47
+ const root = tempRoot("lsp-client-wrapper-nested-");
48
+ const nested = join(root, "parent", "nested");
49
+ mkdirSync(join(nested, "src"), { recursive: true });
50
+ writeFileSync(join(root, "parent", "package.json"), "{}\n");
51
+ writeFileSync(join(nested, "package.json"), "{}\n");
52
+ writeFileSync(join(nested, "src", "file.ts"), "export const value = 1;\n");
53
+
54
+ const workspace = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
55
+ findWorkspaceRoot(join(nested, "src", "file.ts")),
56
+ );
57
+
58
+ expect(workspace).toBe(realpathSync(nested));
59
+ });
60
+
61
+ it("#given four monorepo package files under one .git root #when acquiring clients #then one client serves the whole repository", async () => {
62
+ const root = tempRoot("lsp-client-wrapper-oneclient-");
63
+ mkdirSync(join(root, ".git"), { recursive: true });
64
+ const userConfig = join(root, "user-lsp.json");
65
+ writeFileSync(
66
+ userConfig,
67
+ JSON.stringify({ lsp: { monorepoProbe: { command: [process.execPath], extensions: [".monoroot"] } } }),
68
+ );
69
+ const filePaths = ["alpha", "beta", "gamma", "delta"].map((name) => {
70
+ const packageDir = join(root, "packages", name);
71
+ mkdirSync(join(packageDir, "src"), { recursive: true });
72
+ writeFileSync(join(packageDir, "package.json"), "{}\n");
73
+ const filePath = join(packageDir, "src", "probe.monoroot");
74
+ writeFileSync(filePath, "const value = 1;\n");
75
+ return filePath;
76
+ });
77
+ const factoryRoots: string[] = [];
78
+ const manager = new LspManager({
79
+ clientFactory: (workspaceRoot) => {
80
+ factoryRoots.push(workspaceRoot);
81
+ return new StubLspClient() as unknown as LspClient;
82
+ },
83
+ });
84
+
85
+ try {
86
+ const context = createStandaloneMcpRequestContext({
87
+ cwd: root,
88
+ env: { LSP_TOOLS_MCP_USER_CONFIG: userConfig },
89
+ });
90
+ await Promise.all(
91
+ filePaths.map((filePath) =>
92
+ runWithRequestContext(context, () =>
93
+ withLspClient(filePath, async () => undefined, "diagnostics", { manager }),
94
+ ),
95
+ ),
96
+ );
97
+
98
+ expect(factoryRoots).toEqual([realpathSync(root)]);
99
+ expect(manager.clientCount()).toBe(1);
100
+ } finally {
101
+ await manager.stopAll();
102
+ }
103
+ });
104
+ });
105
+
106
+ class StubLspClient {
107
+ async start(): Promise<void> {}
108
+
109
+ async initialize(): Promise<void> {}
110
+
111
+ isAlive(): boolean {
112
+ return true;
113
+ }
114
+
115
+ command(): string[] {
116
+ return [process.execPath];
117
+ }
118
+
119
+ async stop(): Promise<void> {}
120
+ }
121
+
25
122
  describe("LSP client path confinement", () => {
26
123
  it("#given a relative file inside context cwd #when resolving workspace #then marker search stays inside cwd", () => {
27
124
  const root = tempRoot("lsp-client-wrapper-root-");
@@ -21,7 +21,7 @@ import { LSP_LOCAL_INSTALL_HINTS } from "./server-definitions.js";
21
21
  import { loadInstallDecision } from "./server-install-state.js";
22
22
  import { findServerForExtension } from "./server-resolution.js";
23
23
  import type { ServerLookupResult } from "./types.js";
24
- import { WORKSPACE_MARKERS } from "./workspace-markers.js";
24
+ import { GIT_WORKSPACE_MARKER, PROJECT_WORKSPACE_MARKERS } from "./workspace-markers.js";
25
25
 
26
26
  export function isDirectoryPath(filePath: string): boolean {
27
27
  try {
@@ -31,6 +31,15 @@ export function isDirectoryPath(filePath: string): boolean {
31
31
  }
32
32
  }
33
33
 
34
+ /**
35
+ * Resolves the workspace root for a file inside the request cwd.
36
+ *
37
+ * A `.git`-containing ancestor always wins over nearer package markers, so all packages of a
38
+ * monorepo share one workspace root (one resident client per repository instead of one per
39
+ * package, which multiplies language-server processes by the package count). The nearest
40
+ * package-marked directory is remembered while walking and used only when no `.git` ancestor
41
+ * exists within the request cwd.
42
+ */
34
43
  export function findWorkspaceRoot(filePath: string): string {
35
44
  const cwd = contextCwd();
36
45
  const abs = resolveReadablePathInsideContext(filePath);
@@ -43,20 +52,26 @@ export function findWorkspaceRoot(filePath: string): string {
43
52
  if (!isPathInside(cwd, abs)) return findWorkspaceRootOutsideContext(dir);
44
53
 
45
54
  const fallbackRoot = nearestExistingDirectoryInsideContext(dir, cwd) ?? cwd;
55
+ let nearestPackageRoot: string | undefined;
46
56
  while (isPathInside(cwd, dir)) {
47
57
  const canonicalDir = existingDirectoryInsideContext(dir, cwd);
48
58
  if (canonicalDir !== undefined) {
49
- for (const marker of WORKSPACE_MARKERS) {
50
- if (existsSync(join(dir, marker))) {
51
- return canonicalDir;
52
- }
59
+ if (existsSync(join(dir, GIT_WORKSPACE_MARKER))) {
60
+ return canonicalDir;
61
+ }
62
+ if (nearestPackageRoot === undefined && hasProjectWorkspaceMarker(dir)) {
63
+ nearestPackageRoot = canonicalDir;
53
64
  }
54
65
  }
55
66
  if (dir === cwd) break;
56
67
  dir = dirname(dir);
57
68
  }
58
69
 
59
- return fallbackRoot;
70
+ return nearestPackageRoot ?? fallbackRoot;
71
+ }
72
+
73
+ function hasProjectWorkspaceMarker(directory: string): boolean {
74
+ return PROJECT_WORKSPACE_MARKERS.some((marker) => existsSync(join(directory, marker)));
60
75
  }
61
76
 
62
77
  export function resolveReadablePathInsideContext(filePath: string): string {
@@ -10,3 +10,8 @@ export const MAX_RESIDENT_CLIENTS = 6;
10
10
  export const REAPER_INTERVAL_MS = 60_000;
11
11
  export const STOP_HARD_KILL_TIMEOUT_MS = 5_000;
12
12
  export const STOP_SIGKILL_GRACE_MS = 1_000;
13
+
14
+ /** Max consecutive dead client generations respawned for one key before admission is blocked. */
15
+ export const CLIENT_RESPAWN_RETRY_LIMIT = 2;
16
+ /** How long a spent respawn budget blocks admission before a fresh attempt window opens. */
17
+ export const CLIENT_RESPAWN_COOLDOWN_MS = 60_000;
@@ -69,6 +69,21 @@ export class LspProcessSpawnError extends Error {
69
69
  override readonly name = "LspProcessSpawnError";
70
70
  }
71
71
 
72
+ export class LspClientRespawnBudgetExceededError extends Error {
73
+ override readonly name = "LspClientRespawnBudgetExceededError";
74
+
75
+ constructor(
76
+ readonly serverId: string,
77
+ readonly root: string,
78
+ readonly retryLimit: number,
79
+ ) {
80
+ super(
81
+ `LSP server ${serverId} at ${root} failed to stay alive; respawn budget exhausted ` +
82
+ `(${retryLimit} consecutive dead generations). Retrying after the cooldown may succeed.`,
83
+ );
84
+ }
85
+ }
86
+
72
87
  export function isLspDeadConnectionError(err: unknown): err is LspConnectionClosedError | LspProcessExitedError {
73
88
  return err instanceof LspConnectionClosedError || err instanceof LspProcessExitedError;
74
89
  }