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
package/dist/index.js CHANGED
@@ -5449,7 +5449,7 @@ var init_git_master = __esm(() => {
5449
5449
 
5450
5450
  // packages/omo-config-core/src/schema/memory.ts
5451
5451
  import * as z8 from "zod";
5452
- var OmoMemoryReflectionTriggerSchema, OmoMemoryReflectionSchema, OmoMemorySyncSchema, OmoMemorySearchSchema, OmoMemoryNudgeSchema, OmoMemoryFactsSchema, OmoMemoryDreamSchema, OmoMemoryPeopleSchema, OmoMemorySoulSchema, OmoMemoryWriteNoticeSchema, OmoMemoryReflectionTriggerLayerSchema, OmoMemoryReflectionLayerSchema, OmoMemorySyncLayerSchema, OmoMemorySearchLayerSchema, OmoMemoryNudgeLayerSchema, OmoMemoryFactsLayerSchema, OmoMemoryDreamLayerSchema, OmoMemoryPeopleLayerSchema, OmoMemorySoulLayerSchema, OmoMemoryWriteNoticeLayerSchema, OmoMemoryAgentOverridesSchema, OmoMemorySettingsSchema, OmoMemorySettingsLayerSchema;
5452
+ 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;
5453
5453
  var init_memory = __esm(() => {
5454
5454
  OmoMemoryReflectionTriggerSchema = z8.object({
5455
5455
  step_count: z8.number().int().nonnegative().default(25),
@@ -5470,6 +5470,14 @@ var init_memory = __esm(() => {
5470
5470
  OmoMemorySearchSchema = z8.object({
5471
5471
  enabled: z8.boolean().default(true)
5472
5472
  }).strict();
5473
+ OmoMemoryRecallSchema = z8.object({
5474
+ enabled: z8.boolean().default(true),
5475
+ max_items: z8.number().int().min(1).max(5).default(2),
5476
+ budget_tokens: z8.number().int().positive().default(600),
5477
+ excerpt_chars: z8.number().int().positive().default(200),
5478
+ min_score: z8.number().optional(),
5479
+ exclude: z8.array(z8.string()).default([])
5480
+ }).strict();
5473
5481
  OmoMemoryNudgeSchema = z8.object({
5474
5482
  enabled: z8.boolean().default(true),
5475
5483
  every_user_turns: z8.number().int().min(1).default(10)
@@ -5516,6 +5524,14 @@ var init_memory = __esm(() => {
5516
5524
  OmoMemorySearchLayerSchema = z8.object({
5517
5525
  enabled: z8.boolean().optional()
5518
5526
  }).strict();
5527
+ OmoMemoryRecallLayerSchema = z8.object({
5528
+ enabled: z8.boolean().optional(),
5529
+ max_items: z8.number().int().min(1).max(5).optional(),
5530
+ budget_tokens: z8.number().int().positive().optional(),
5531
+ excerpt_chars: z8.number().int().positive().optional(),
5532
+ min_score: z8.number().optional(),
5533
+ exclude: z8.array(z8.string()).optional()
5534
+ }).strict();
5519
5535
  OmoMemoryNudgeLayerSchema = z8.object({
5520
5536
  enabled: z8.boolean().optional(),
5521
5537
  every_user_turns: z8.number().int().min(1).optional()
@@ -5555,6 +5571,7 @@ var init_memory = __esm(() => {
5555
5571
  write_notice: OmoMemoryWriteNoticeLayerSchema.optional(),
5556
5572
  sync: OmoMemorySyncLayerSchema.optional(),
5557
5573
  search: OmoMemorySearchLayerSchema.optional(),
5574
+ recall: OmoMemoryRecallLayerSchema.optional(),
5558
5575
  compile_warn_tokens: z8.number().int().positive().optional()
5559
5576
  }).strict();
5560
5577
  OmoMemorySettingsSchema = z8.object({
@@ -5584,6 +5601,13 @@ var init_memory = __esm(() => {
5584
5601
  write_notice: OmoMemoryWriteNoticeSchema.default({ enabled: true }),
5585
5602
  sync: OmoMemorySyncSchema.default({ enabled: true }),
5586
5603
  search: OmoMemorySearchSchema.default({ enabled: true }),
5604
+ recall: OmoMemoryRecallSchema.default({
5605
+ enabled: true,
5606
+ max_items: 2,
5607
+ budget_tokens: 600,
5608
+ excerpt_chars: 200,
5609
+ exclude: []
5610
+ }),
5587
5611
  compile_warn_tokens: z8.number().int().positive().default(30000),
5588
5612
  agents: z8.record(z8.string(), OmoMemoryAgentOverridesSchema).default({})
5589
5613
  }).strict();
@@ -5600,6 +5624,7 @@ var init_memory = __esm(() => {
5600
5624
  write_notice: OmoMemoryWriteNoticeLayerSchema.optional(),
5601
5625
  sync: OmoMemorySyncLayerSchema.optional(),
5602
5626
  search: OmoMemorySearchLayerSchema.optional(),
5627
+ recall: OmoMemoryRecallLayerSchema.optional(),
5603
5628
  compile_warn_tokens: z8.number().int().positive().optional(),
5604
5629
  agents: z8.record(z8.string(), OmoMemoryAgentOverridesSchema).optional()
5605
5630
  }).strict();
@@ -5634,11 +5659,11 @@ function resolveOmoTaskSettings(input, resolveParallelism = availableParallelism
5634
5659
  const record8 = z10.record(z10.string(), z10.unknown()).parse(input);
5635
5660
  return OmoTaskSettingsSchema.parse({
5636
5661
  ...record8,
5637
- residency_max_children: record8["residency_max_children"] ?? Math.max(8, resolveParallelism() * 3),
5662
+ residency_max_children: record8["residency_max_children"] ?? Math.min(DEFAULT_RESIDENCY_MAX_CHILDREN, Math.max(8, resolveParallelism() * 2)),
5638
5663
  global_concurrency: record8["global_concurrency"] ?? Math.max(8, resolveParallelism() * 2)
5639
5664
  });
5640
5665
  }
5641
- var ResidencyMaxChildrenInputSchema, OmoTaskWaitSchema, OmoTaskTeamSettingsSchema, OmoTaskWarningsSchema, OmoTaskDagSettingsSchema, OmoTaskSettingsSchema, OmoTaskDagSettingsLayerSchema, OmoTaskWaitLayerSchema, OmoTaskTeamSettingsLayerSchema, OmoTaskWarningsLayerSchema, OmoTaskSettingsLayerSchema;
5666
+ var DEFAULT_RESIDENCY_MAX_CHILDREN = 16, ResidencyMaxChildrenInputSchema, OmoTaskWaitSchema, OmoTaskTeamSettingsSchema, OmoTaskWarningsSchema, OmoTaskDagSettingsSchema, OmoTaskSettingsSchema, OmoTaskDagSettingsLayerSchema, OmoTaskWaitLayerSchema, OmoTaskTeamSettingsLayerSchema, OmoTaskWarningsLayerSchema, OmoTaskSettingsLayerSchema;
5642
5667
  var init_task = __esm(() => {
5643
5668
  ResidencyMaxChildrenInputSchema = z10.union([z10.number().int().nonnegative(), z10.literal("unlimited")]);
5644
5669
  OmoTaskWaitSchema = z10.object({
@@ -11999,7 +12024,6 @@ function extractPromptFailureMessage(error) {
11999
12024
  try {
12000
12025
  return JSON.stringify(error);
12001
12026
  } catch (stringifyError) {
12002
- stringifyError instanceof Error;
12003
12027
  return "";
12004
12028
  }
12005
12029
  }
@@ -24366,6 +24390,33 @@ var AGENT_MODEL_REQUIREMENTS = {
24366
24390
  { providers: ["opencode-go"], model: "glm-5.2" }
24367
24391
  ]
24368
24392
  },
24393
+ "omo-senpi-code-reviewer": {
24394
+ fallbackChain: [
24395
+ { providers: ["openai", "openai-codex"], model: "gpt-5.6-terra", variant: "medium" },
24396
+ { providers: ["github-copilot"], model: "gpt-5.6-terra", variant: "medium" },
24397
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
24398
+ { providers: ["opencode-go"], model: "glm-5.2" },
24399
+ { providers: ["kimi-for-coding"], model: "kimi-k3" }
24400
+ ]
24401
+ },
24402
+ "omo-senpi-qa-executor": {
24403
+ fallbackChain: [
24404
+ { providers: ["openai", "openai-codex"], model: "gpt-5.6-luna", variant: "high" },
24405
+ { providers: ["github-copilot"], model: "gpt-5.6-luna", variant: "high" },
24406
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
24407
+ { providers: ["opencode-go"], model: "glm-5.2" },
24408
+ { providers: ["kimi-for-coding"], model: "kimi-k3" }
24409
+ ]
24410
+ },
24411
+ "omo-senpi-gate-reviewer": {
24412
+ fallbackChain: [
24413
+ { providers: ["openai", "openai-codex"], model: "gpt-5.6-sol", variant: "low" },
24414
+ { providers: ["github-copilot"], model: "gpt-5.6-sol", variant: "low" },
24415
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-5", variant: "max" },
24416
+ { providers: ["opencode-go"], model: "glm-5.2" },
24417
+ { providers: ["kimi-for-coding"], model: "kimi-k3" }
24418
+ ]
24419
+ },
24369
24420
  atlas: {
24370
24421
  fallbackChain: [
24371
24422
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-5" },
@@ -24955,7 +25006,7 @@ function normalizeCapabilitiesReasoningEfforts(capabilities) {
24955
25006
  }
24956
25007
  return capabilities.reasoningEfforts.map((value) => value.toLowerCase());
24957
25008
  }
24958
- function resolveField(normalized, familyCaps, ladder, familyKnown, metadataOverride, familyAliases) {
25009
+ function resolveField(normalized, familyCaps, familyKnown, metadataOverride, familyAliases) {
24959
25010
  const aliased = familyAliases?.[normalized];
24960
25011
  if (aliased && (metadataOverride?.includes(aliased) || familyCaps?.includes(aliased))) {
24961
25012
  return { value: aliased, reason: "unsupported-by-model-family" };
@@ -24990,7 +25041,7 @@ function resolveCompatibleModelSettings(input) {
24990
25041
  let variant = input.desired.variant;
24991
25042
  if (variant !== undefined) {
24992
25043
  const normalized = variant.toLowerCase();
24993
- const resolved = resolveField(normalized, family?.variants, REASONING_LEVELS2, familyKnown, metadataVariants);
25044
+ const resolved = resolveField(normalized, family?.variants, familyKnown, metadataVariants);
24994
25045
  if (resolved.value !== normalized && resolved.reason) {
24995
25046
  changes.push({ field: "variant", from: variant, to: resolved.value, reason: resolved.reason });
24996
25047
  }
@@ -24999,7 +25050,7 @@ function resolveCompatibleModelSettings(input) {
24999
25050
  let reasoningEffort = input.desired.reasoningEffort;
25000
25051
  if (reasoningEffort !== undefined) {
25001
25052
  const normalized = reasoningEffort.toLowerCase();
25002
- const resolved = resolveField(normalized, family?.reasoningEfforts, REASONING_LEVELS2, familyKnown, metadataReasoningEfforts, family?.reasoningEffortAliases);
25053
+ const resolved = resolveField(normalized, family?.reasoningEfforts, familyKnown, metadataReasoningEfforts, family?.reasoningEffortAliases);
25003
25054
  if (resolved.value !== normalized && resolved.reason) {
25004
25055
  changes.push({ field: "reasoningEffort", from: reasoningEffort, to: resolved.value, reason: resolved.reason });
25005
25056
  }
@@ -27343,6 +27394,7 @@ var BackgroundTaskConfigSchema = z20.object({
27343
27394
  providerConcurrency: z20.record(z20.string(), z20.number().min(0)).optional(),
27344
27395
  modelConcurrency: z20.record(z20.string(), z20.number().min(0)).optional(),
27345
27396
  maxDepth: z20.number().int().min(1).optional(),
27397
+ maxLiveDescendantsPerRoot: z20.number().int().min(0).optional(),
27346
27398
  staleTimeoutMs: z20.number().min(60000).optional(),
27347
27399
  messageStalenessTimeoutMs: z20.number().min(60000).optional(),
27348
27400
  taskTtlMs: z20.number().min(300000).optional(),
@@ -85494,7 +85546,7 @@ var getUrl = ({ baseUrl, path: path7, query, querySerializer, url: _url }) => {
85494
85546
  };
85495
85547
 
85496
85548
  // node_modules/.bun/@opencode-ai+sdk@1.18.22/node_modules/@opencode-ai/sdk/dist/gen/client/utils.gen.js
85497
- var createQuerySerializer = ({ allowReserved, array: array7, object: object15 } = {}) => {
85549
+ var createQuerySerializer = ({ allowReserved, array: array8, object: object15 } = {}) => {
85498
85550
  const querySerializer = (queryParams) => {
85499
85551
  const search = [];
85500
85552
  if (queryParams && typeof queryParams === "object") {
@@ -85510,7 +85562,7 @@ var createQuerySerializer = ({ allowReserved, array: array7, object: object15 }
85510
85562
  name,
85511
85563
  style: "form",
85512
85564
  value,
85513
- ...array7
85565
+ ...array8
85514
85566
  });
85515
85567
  if (serializedArray)
85516
85568
  search.push(serializedArray);
@@ -87109,7 +87161,6 @@ function extractMessage2(error) {
87109
87161
  try {
87110
87162
  return JSON.stringify(error);
87111
87163
  } catch (stringifyError) {
87112
- stringifyError instanceof Error;
87113
87164
  return "";
87114
87165
  }
87115
87166
  }
@@ -90765,7 +90816,6 @@ async function hasIncompleteTodos(ctx, sessionID) {
90765
90816
  return false;
90766
90817
  return todos.some((todo) => todo.status !== "completed" && todo.status !== "cancelled");
90767
90818
  } catch (todoError) {
90768
- todoError instanceof Error;
90769
90819
  return false;
90770
90820
  }
90771
90821
  }
@@ -92550,9 +92600,7 @@ async function findReadmeMdUp(input) {
92550
92600
  try {
92551
92601
  await access2(readmePath);
92552
92602
  found.push(readmePath);
92553
- } catch (error) {
92554
- error instanceof Error;
92555
- }
92603
+ } catch (error) {}
92556
92604
  if (current === input.rootDir)
92557
92605
  break;
92558
92606
  const parent = dirname23(current);
@@ -92934,6 +92982,21 @@ var TRUNCATE_CONFIG = {
92934
92982
  charsPerToken: 4
92935
92983
  };
92936
92984
 
92985
+ // packages/omo-opencode/src/hooks/anthropic-context-window-limit-recovery/session-timeout-map.ts
92986
+ function clearSessionTimeout(timeoutBySession, sessionID) {
92987
+ const timeoutID = timeoutBySession.get(sessionID);
92988
+ if (timeoutID !== undefined) {
92989
+ clearTimeout(timeoutID);
92990
+ timeoutBySession.delete(sessionID);
92991
+ }
92992
+ }
92993
+ function clearAllSessionTimeouts(timeoutBySession) {
92994
+ for (const timeoutID of timeoutBySession.values()) {
92995
+ clearTimeout(timeoutID);
92996
+ }
92997
+ timeoutBySession.clear();
92998
+ }
92999
+
92937
93000
  // packages/omo-opencode/src/hooks/anthropic-context-window-limit-recovery/state.ts
92938
93001
  function getOrCreateRetryState(autoCompactState, sessionID) {
92939
93002
  let state2 = autoCompactState.retryStateBySession.get(sessionID);
@@ -92964,6 +93027,15 @@ function clearSessionState(autoCompactState, sessionID) {
92964
93027
  autoCompactState.emptyContentAttemptBySession.delete(sessionID);
92965
93028
  autoCompactState.compactionInProgress.delete(sessionID);
92966
93029
  }
93030
+ function clearAllSessionState(autoCompactState) {
93031
+ clearAllSessionTimeouts(autoCompactState.retryTimerBySession);
93032
+ autoCompactState.pendingCompact.clear();
93033
+ autoCompactState.errorDataBySession.clear();
93034
+ autoCompactState.retryStateBySession.clear();
93035
+ autoCompactState.truncateStateBySession.clear();
93036
+ autoCompactState.emptyContentAttemptBySession.clear();
93037
+ autoCompactState.compactionInProgress.clear();
93038
+ }
92967
93039
  function setRetryTimer(autoCompactState, sessionID, timeout) {
92968
93040
  const existingTimer = autoCompactState.retryTimerBySession.get(sessionID);
92969
93041
  if (existingTimer !== undefined) {
@@ -93798,7 +93870,7 @@ function getSdkMessages(response) {
93798
93870
  const data = record11["data"];
93799
93871
  if (Array.isArray(data))
93800
93872
  return data;
93801
- return Array.isArray(record11) ? record11 : [];
93873
+ return [];
93802
93874
  }
93803
93875
  async function findEmptyMessagesFromSDK(client3, sessionID) {
93804
93876
  try {
@@ -94515,21 +94587,6 @@ async function attemptDeduplicationRecovery(sessionID, parsed, experimental2, cl
94515
94587
  }
94516
94588
  }
94517
94589
 
94518
- // packages/omo-opencode/src/hooks/anthropic-context-window-limit-recovery/session-timeout-map.ts
94519
- function clearSessionTimeout(timeoutBySession, sessionID) {
94520
- const timeoutID = timeoutBySession.get(sessionID);
94521
- if (timeoutID !== undefined) {
94522
- clearTimeout(timeoutID);
94523
- timeoutBySession.delete(sessionID);
94524
- }
94525
- }
94526
- function clearAllSessionTimeouts(timeoutBySession) {
94527
- for (const timeoutID of timeoutBySession.values()) {
94528
- clearTimeout(timeoutID);
94529
- }
94530
- timeoutBySession.clear();
94531
- }
94532
-
94533
94590
  // packages/omo-opencode/src/hooks/anthropic-context-window-limit-recovery/recovery-hook.ts
94534
94591
  init_logger2();
94535
94592
  function createRecoveryState() {
@@ -94655,7 +94712,7 @@ function createAnthropicContextWindowLimitRecoveryHook(ctx, options) {
94655
94712
  event: eventHandler,
94656
94713
  dispose: () => {
94657
94714
  clearAllSessionTimeouts(pendingCompactionTimeoutBySession);
94658
- clearAllSessionTimeouts(autoCompactState.retryTimerBySession);
94715
+ clearAllSessionState(autoCompactState);
94659
94716
  }
94660
94717
  };
94661
94718
  }
@@ -95922,7 +95979,9 @@ function appendTranscriptEntry(sessionId, entry) {
95922
95979
  appendFileSync6(path8, line);
95923
95980
  }
95924
95981
  var TRANSCRIPT_CACHE_TTL_MS = 5 * 60 * 1000;
95982
+ var TRANSCRIPT_CACHE_PRUNE_INTERVAL_MS = TRANSCRIPT_CACHE_TTL_MS;
95925
95983
  var transcriptCache = new Map;
95984
+ var cleanupInterval2 = null;
95926
95985
  function logTranscriptError(message, error) {
95927
95986
  log2(message, { error });
95928
95987
  }
@@ -95958,6 +96017,30 @@ function clearTranscriptCache(sessionId) {
95958
96017
  transcriptCache.clear();
95959
96018
  }
95960
96019
  }
96020
+ function pruneExpiredTranscriptCache() {
96021
+ const now = Date.now();
96022
+ for (const sessionId of [...transcriptCache.keys()]) {
96023
+ const entry = transcriptCache.get(sessionId);
96024
+ if (!entry || now - entry.createdAt >= TRANSCRIPT_CACHE_TTL_MS) {
96025
+ clearTranscriptCache(sessionId);
96026
+ }
96027
+ }
96028
+ }
96029
+ function ensureTranscriptCacheCleanup() {
96030
+ if (cleanupInterval2)
96031
+ return;
96032
+ cleanupInterval2 = setInterval(pruneExpiredTranscriptCache, TRANSCRIPT_CACHE_PRUNE_INTERVAL_MS);
96033
+ if (typeof cleanupInterval2 === "object" && "unref" in cleanupInterval2) {
96034
+ cleanupInterval2.unref();
96035
+ }
96036
+ }
96037
+ function stopTranscriptCacheCleanup() {
96038
+ clearTranscriptCache();
96039
+ if (!cleanupInterval2)
96040
+ return;
96041
+ clearInterval(cleanupInterval2);
96042
+ cleanupInterval2 = null;
96043
+ }
95961
96044
  function isCacheValid(entry) {
95962
96045
  return Date.now() - entry.createdAt < TRANSCRIPT_CACHE_TTL_MS;
95963
96046
  }
@@ -96034,6 +96117,7 @@ async function buildTranscriptFromSession(client3, sessionId, directory, current
96034
96117
  tempPath: null,
96035
96118
  createdAt: Date.now()
96036
96119
  });
96120
+ ensureTranscriptCacheCleanup();
96037
96121
  }
96038
96122
  const allEntries = [...baseEntries, buildCurrentEntry(currentToolName, currentToolInput)];
96039
96123
  if (previousTempPath) {
@@ -96091,6 +96175,91 @@ function deleteTempTranscript(path8) {
96091
96175
  }
96092
96176
  }
96093
96177
 
96178
+ // packages/omo-opencode/src/hooks/claude-code-hooks/todo.ts
96179
+ import { join as join77 } from "path";
96180
+ var TODO_DIR = join77(getClaudeConfigDir(), "todos");
96181
+ function getTodoPath(sessionId) {
96182
+ return join77(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
96183
+ }
96184
+
96185
+ // packages/omo-opencode/src/hooks/claude-code-hooks/stop.ts
96186
+ var stopHookActiveState = new Map;
96187
+ function clearStopHookActive(sessionId) {
96188
+ stopHookActiveState.delete(sessionId);
96189
+ }
96190
+ function clearAllStopHookActive() {
96191
+ stopHookActiveState.clear();
96192
+ }
96193
+ async function executeStopHooks(ctx, config3, extendedConfig) {
96194
+ if (ctx.parentSessionId) {
96195
+ return { block: false };
96196
+ }
96197
+ if (!config3) {
96198
+ return { block: false };
96199
+ }
96200
+ const matchers = findMatchingHooks(config3, "Stop");
96201
+ if (matchers.length === 0) {
96202
+ return { block: false };
96203
+ }
96204
+ const stdinData = {
96205
+ session_id: ctx.sessionId,
96206
+ transcript_path: ctx.transcriptPath,
96207
+ cwd: ctx.cwd,
96208
+ permission_mode: ctx.permissionMode ?? "bypassPermissions",
96209
+ hook_event_name: "Stop",
96210
+ stop_hook_active: stopHookActiveState.get(ctx.sessionId) ?? false,
96211
+ todo_path: getTodoPath(ctx.sessionId),
96212
+ hook_source: "opencode-plugin"
96213
+ };
96214
+ for (const matcher of matchers) {
96215
+ if (!matcher.hooks || matcher.hooks.length === 0)
96216
+ continue;
96217
+ for (const hook of matcher.hooks) {
96218
+ if (hook.type !== "command" && hook.type !== "http")
96219
+ continue;
96220
+ const hookName = getHookIdentifier(hook);
96221
+ if (isHookCommandDisabled("Stop", hookName, extendedConfig ?? null)) {
96222
+ log2("Stop hook command skipped (disabled by config)", { command: hookName });
96223
+ continue;
96224
+ }
96225
+ const result = await dispatchHook(hook, JSON.stringify(stdinData), ctx.cwd);
96226
+ if (result.exitCode === 2) {
96227
+ const reason = normalizeHookText(result.stderr) ?? normalizeHookText(result.stdout) ?? "Blocked by stop hook";
96228
+ return {
96229
+ block: true,
96230
+ reason,
96231
+ injectPrompt: reason
96232
+ };
96233
+ }
96234
+ if (result.stdout) {
96235
+ try {
96236
+ const output = JSON.parse(result.stdout || "{}");
96237
+ if (output.stop_hook_active !== undefined) {
96238
+ stopHookActiveState.set(ctx.sessionId, output.stop_hook_active);
96239
+ }
96240
+ const isBlock = output.decision === "block";
96241
+ if (isBlock) {
96242
+ const reason = normalizeHookText(output.reason);
96243
+ const injectPrompt = normalizeHookText(output.inject_prompt) ?? reason;
96244
+ return {
96245
+ block: true,
96246
+ reason,
96247
+ stopHookActive: output.stop_hook_active,
96248
+ permissionMode: output.permission_mode,
96249
+ injectPrompt
96250
+ };
96251
+ }
96252
+ } catch (error) {
96253
+ if (!(error instanceof SyntaxError)) {
96254
+ throw error;
96255
+ }
96256
+ }
96257
+ }
96258
+ }
96259
+ }
96260
+ return { block: false };
96261
+ }
96262
+
96094
96263
  // packages/omo-opencode/src/hooks/claude-code-hooks/session-hook-state.ts
96095
96264
  var sessionFirstMessageProcessed = new Set;
96096
96265
  var sessionErrorState = new Map;
@@ -96099,10 +96268,16 @@ function clearSessionHookState(sessionID) {
96099
96268
  sessionErrorState.delete(sessionID);
96100
96269
  sessionInterruptState.delete(sessionID);
96101
96270
  }
96271
+ function clearSessionEndHookState(sessionID) {
96272
+ clearSessionHookState(sessionID);
96273
+ sessionFirstMessageProcessed.delete(sessionID);
96274
+ clearStopHookActive(sessionID);
96275
+ }
96102
96276
  function clearAllSessionHookState() {
96103
96277
  sessionErrorState.clear();
96104
96278
  sessionInterruptState.clear();
96105
96279
  sessionFirstMessageProcessed.clear();
96280
+ clearAllStopHookActive();
96106
96281
  }
96107
96282
 
96108
96283
  // packages/omo-opencode/src/hooks/claude-code-hooks/handlers/chat-message-handler.ts
@@ -96313,89 +96488,10 @@ function createPreCompactHandler(ctx, config3) {
96313
96488
  };
96314
96489
  }
96315
96490
 
96316
- // packages/omo-opencode/src/hooks/claude-code-hooks/todo.ts
96317
- import { join as join77 } from "path";
96318
- var TODO_DIR = join77(getClaudeConfigDir(), "todos");
96319
- function getTodoPath(sessionId) {
96320
- return join77(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
96321
- }
96322
-
96323
- // packages/omo-opencode/src/hooks/claude-code-hooks/stop.ts
96324
- var stopHookActiveState = new Map;
96325
- async function executeStopHooks(ctx, config3, extendedConfig) {
96326
- if (ctx.parentSessionId) {
96327
- return { block: false };
96328
- }
96329
- if (!config3) {
96330
- return { block: false };
96331
- }
96332
- const matchers = findMatchingHooks(config3, "Stop");
96333
- if (matchers.length === 0) {
96334
- return { block: false };
96335
- }
96336
- const stdinData = {
96337
- session_id: ctx.sessionId,
96338
- transcript_path: ctx.transcriptPath,
96339
- cwd: ctx.cwd,
96340
- permission_mode: ctx.permissionMode ?? "bypassPermissions",
96341
- hook_event_name: "Stop",
96342
- stop_hook_active: stopHookActiveState.get(ctx.sessionId) ?? false,
96343
- todo_path: getTodoPath(ctx.sessionId),
96344
- hook_source: "opencode-plugin"
96345
- };
96346
- for (const matcher of matchers) {
96347
- if (!matcher.hooks || matcher.hooks.length === 0)
96348
- continue;
96349
- for (const hook of matcher.hooks) {
96350
- if (hook.type !== "command" && hook.type !== "http")
96351
- continue;
96352
- const hookName = getHookIdentifier(hook);
96353
- if (isHookCommandDisabled("Stop", hookName, extendedConfig ?? null)) {
96354
- log2("Stop hook command skipped (disabled by config)", { command: hookName });
96355
- continue;
96356
- }
96357
- const result = await dispatchHook(hook, JSON.stringify(stdinData), ctx.cwd);
96358
- if (result.exitCode === 2) {
96359
- const reason = normalizeHookText(result.stderr) ?? normalizeHookText(result.stdout) ?? "Blocked by stop hook";
96360
- return {
96361
- block: true,
96362
- reason,
96363
- injectPrompt: reason
96364
- };
96365
- }
96366
- if (result.stdout) {
96367
- try {
96368
- const output = JSON.parse(result.stdout || "{}");
96369
- if (output.stop_hook_active !== undefined) {
96370
- stopHookActiveState.set(ctx.sessionId, output.stop_hook_active);
96371
- }
96372
- const isBlock = output.decision === "block";
96373
- if (isBlock) {
96374
- const reason = normalizeHookText(output.reason);
96375
- const injectPrompt = normalizeHookText(output.inject_prompt) ?? reason;
96376
- return {
96377
- block: true,
96378
- reason,
96379
- stopHookActive: output.stop_hook_active,
96380
- permissionMode: output.permission_mode,
96381
- injectPrompt
96382
- };
96383
- }
96384
- } catch (error) {
96385
- if (!(error instanceof SyntaxError)) {
96386
- throw error;
96387
- }
96388
- }
96389
- }
96390
- }
96391
- }
96392
- return { block: false };
96393
- }
96394
-
96395
96491
  // packages/omo-opencode/src/hooks/claude-code-hooks/tool-input-cache.ts
96396
96492
  var cache = new Map;
96397
96493
  var CACHE_TTL = 60000;
96398
- var cleanupInterval2 = null;
96494
+ var cleanupInterval3 = null;
96399
96495
  function pruneExpiredToolInputs() {
96400
96496
  const now = Date.now();
96401
96497
  for (const [key, entry] of cache.entries()) {
@@ -96405,13 +96501,13 @@ function pruneExpiredToolInputs() {
96405
96501
  }
96406
96502
  }
96407
96503
  function ensureCleanupInterval() {
96408
- if (cleanupInterval2)
96504
+ if (cleanupInterval3)
96409
96505
  return;
96410
- cleanupInterval2 = setInterval(() => {
96506
+ cleanupInterval3 = setInterval(() => {
96411
96507
  pruneExpiredToolInputs();
96412
96508
  }, CACHE_TTL);
96413
- if (typeof cleanupInterval2 === "object" && "unref" in cleanupInterval2) {
96414
- cleanupInterval2.unref();
96509
+ if (typeof cleanupInterval3 === "object" && "unref" in cleanupInterval3) {
96510
+ cleanupInterval3.unref();
96415
96511
  }
96416
96512
  }
96417
96513
  function cacheToolInput(sessionId, toolName, invocationId, toolInput) {
@@ -96443,10 +96539,10 @@ function clearToolInputCache(sessionId) {
96443
96539
  }
96444
96540
  function stopToolInputCacheCleanup() {
96445
96541
  clearToolInputCache();
96446
- if (!cleanupInterval2)
96542
+ if (!cleanupInterval3)
96447
96543
  return;
96448
- clearInterval(cleanupInterval2);
96449
- cleanupInterval2 = null;
96544
+ clearInterval(cleanupInterval3);
96545
+ cleanupInterval3 = null;
96450
96546
  }
96451
96547
 
96452
96548
  // packages/omo-opencode/src/hooks/claude-code-hooks/handlers/session-event-handler.ts
@@ -96473,7 +96569,7 @@ function createSessionEventHandler(ctx, config3, contextCollector) {
96473
96569
  clearTranscriptCache(sessionID2);
96474
96570
  clearToolInputCache(sessionID2);
96475
96571
  contextCollector?.clear(sessionID2);
96476
- clearSessionHookState(sessionID2);
96572
+ clearSessionEndHookState(sessionID2);
96477
96573
  }
96478
96574
  return;
96479
96575
  }
@@ -96563,7 +96659,7 @@ function createSessionEventHandler(ctx, config3, contextCollector) {
96563
96659
  };
96564
96660
  }
96565
96661
  function disposeSessionEventHandler(contextCollector) {
96566
- clearTranscriptCache();
96662
+ stopTranscriptCacheCleanup();
96567
96663
  clearClaudeHooksConfigCache();
96568
96664
  clearPluginExtendedConfigCache();
96569
96665
  stopToolInputCacheCleanup();
@@ -98631,7 +98727,6 @@ function getLocalDevVersion(directory) {
98631
98727
  const pkg = JSON.parse(content);
98632
98728
  return pkg.version ?? null;
98633
98729
  } catch (error) {
98634
- error instanceof Error;
98635
98730
  return null;
98636
98731
  }
98637
98732
  }
@@ -98661,7 +98756,6 @@ function findPluginEntry(directory) {
98661
98756
  }
98662
98757
  }
98663
98758
  } catch (error) {
98664
- error instanceof Error;
98665
98759
  continue;
98666
98760
  }
98667
98761
  }
@@ -98727,7 +98821,7 @@ function getCachedVersion(options = {}) {
98727
98821
  // package.json
98728
98822
  var package_default = {
98729
98823
  name: "oh-my-opencode",
98730
- version: "5.0.0-beta.30",
98824
+ version: "5.0.0-beta.31",
98731
98825
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
98732
98826
  main: "./dist/index.js",
98733
98827
  types: "dist/index.d.ts",
@@ -98908,7 +99002,7 @@ var package_default = {
98908
99002
  zod: "^4.4.3"
98909
99003
  },
98910
99004
  devDependencies: {
98911
- "@code-yeongyu/senpi": "2026.8.30-3",
99005
+ "@code-yeongyu/senpi": "2026.8.31",
98912
99006
  "@oh-my-opencode/agents-md-core": "workspace:*",
98913
99007
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
98914
99008
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -98949,18 +99043,18 @@ var package_default = {
98949
99043
  typescript: "^7.0.2"
98950
99044
  },
98951
99045
  optionalDependencies: {
98952
- "oh-my-opencode-darwin-arm64": "5.0.0-beta.30",
98953
- "oh-my-opencode-darwin-x64": "5.0.0-beta.30",
98954
- "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.30",
98955
- "oh-my-opencode-linux-arm64": "5.0.0-beta.30",
98956
- "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.30",
98957
- "oh-my-opencode-linux-x64": "5.0.0-beta.30",
98958
- "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.30",
98959
- "oh-my-opencode-linux-x64-musl": "5.0.0-beta.30",
98960
- "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.30",
98961
- "oh-my-opencode-windows-arm64": "5.0.0-beta.30",
98962
- "oh-my-opencode-windows-x64": "5.0.0-beta.30",
98963
- "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.30"
99046
+ "oh-my-opencode-darwin-arm64": "5.0.0-beta.31",
99047
+ "oh-my-opencode-darwin-x64": "5.0.0-beta.31",
99048
+ "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.31",
99049
+ "oh-my-opencode-linux-arm64": "5.0.0-beta.31",
99050
+ "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.31",
99051
+ "oh-my-opencode-linux-x64": "5.0.0-beta.31",
99052
+ "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.31",
99053
+ "oh-my-opencode-linux-x64-musl": "5.0.0-beta.31",
99054
+ "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.31",
99055
+ "oh-my-opencode-windows-arm64": "5.0.0-beta.31",
99056
+ "oh-my-opencode-windows-x64": "5.0.0-beta.31",
99057
+ "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.31"
98964
99058
  },
98965
99059
  overrides: {
98966
99060
  "@earendil-works/pi-agent-core": "0.84.2",
@@ -98978,7 +99072,7 @@ var package_default = {
98978
99072
  "@code-yeongyu/comment-checker"
98979
99073
  ],
98980
99074
  patchedDependencies: {
98981
- "@code-yeongyu/senpi@2026.8.30-3": "patches/@code-yeongyu%2Fsenpi@2026.8.30-3.patch"
99075
+ "@code-yeongyu/senpi@2026.8.31": "patches/@code-yeongyu%2Fsenpi@2026.8.31.patch"
98982
99076
  }
98983
99077
  };
98984
99078
 
@@ -99002,7 +99096,6 @@ async function getLatestVersion(channel = "latest") {
99002
99096
  const data = await response.json();
99003
99097
  return data[channel] ?? data.latest ?? null;
99004
99098
  } catch (error) {
99005
- error instanceof Error;
99006
99099
  return null;
99007
99100
  } finally {
99008
99101
  clearTimeout(timeoutId);
@@ -101041,8 +101134,7 @@ var TARGET_TOOLS = new Set([
101041
101134
  ]);
101042
101135
  var AGENT_TOOLS = new Set([
101043
101136
  "task",
101044
- "call_omo_agent",
101045
- "task"
101137
+ "call_omo_agent"
101046
101138
  ]);
101047
101139
  var REMINDER_MESSAGE = `
101048
101140
  [Agent Usage Reminder]
@@ -101082,7 +101174,6 @@ function loadAgentUsageState(sessionID) {
101082
101174
  const content = readFileSync48(filePath, "utf-8");
101083
101175
  return JSON.parse(content);
101084
101176
  } catch (error) {
101085
- error instanceof Error;
101086
101177
  return null;
101087
101178
  }
101088
101179
  }
@@ -106136,6 +106227,23 @@ function extractPromptText2(parts) {
106136
106227
  }
106137
106228
  // packages/omo-opencode/src/hooks/keyword-detector/hook.ts
106138
106229
  var defaultModeUltraworkInjectedSessions = new Set;
106230
+ var DEFAULT_MODE_ULTRAWORK_SESSION_CAP = 256;
106231
+ function rememberDefaultModeUltraworkInjectedSession(sessionID) {
106232
+ if (defaultModeUltraworkInjectedSessions.has(sessionID))
106233
+ return;
106234
+ if (defaultModeUltraworkInjectedSessions.size >= DEFAULT_MODE_ULTRAWORK_SESSION_CAP) {
106235
+ const oldest = defaultModeUltraworkInjectedSessions.values().next().value;
106236
+ if (oldest !== undefined)
106237
+ defaultModeUltraworkInjectedSessions.delete(oldest);
106238
+ }
106239
+ defaultModeUltraworkInjectedSessions.add(sessionID);
106240
+ }
106241
+ function clearDefaultModeUltraworkInjectedSession(sessionID) {
106242
+ defaultModeUltraworkInjectedSessions.delete(sessionID);
106243
+ }
106244
+ function clearAllDefaultModeUltraworkInjectedSessions() {
106245
+ defaultModeUltraworkInjectedSessions.clear();
106246
+ }
106139
106247
  function suppressComboStandalones(detected) {
106140
106248
  const hasCombo = detected.some((k) => k.type === "hyperplan-ultrawork");
106141
106249
  if (!hasCombo)
@@ -106196,7 +106304,7 @@ function createKeywordDetectorHook(ctx, _collector, _ralphLoop, config3, default
106196
106304
  const isNonMainSession = mainSessionID && input.sessionID !== mainSessionID;
106197
106305
  if (detectedKeywords.length === 0) {
106198
106306
  if (defaultMode?.ultrawork && !isNonMainSession && !defaultModeUltraworkInjectedSessions.has(input.sessionID)) {
106199
- defaultModeUltraworkInjectedSessions.add(input.sessionID);
106307
+ rememberDefaultModeUltraworkInjectedSession(input.sessionID);
106200
106308
  log2(`[keyword-detector] Default ultrawork mode auto-activated (injected via system prompt)`, { sessionID: input.sessionID });
106201
106309
  ctx.client.tui.showToast({
106202
106310
  body: {
@@ -106294,6 +106402,16 @@ ${allMessages}`;
106294
106402
  sessionID: input.sessionID,
106295
106403
  types: detectedKeywords.map((k) => k.type)
106296
106404
  });
106405
+ },
106406
+ event: ({ event }) => {
106407
+ if (event.type !== "session.deleted")
106408
+ return;
106409
+ const sessionID = resolveSessionEventID(event.properties);
106410
+ if (sessionID)
106411
+ clearDefaultModeUltraworkInjectedSession(sessionID);
106412
+ },
106413
+ dispose: () => {
106414
+ clearAllDefaultModeUltraworkInjectedSessions();
106297
106415
  }
106298
106416
  };
106299
106417
  }
@@ -119738,8 +119856,6 @@ async function runPreemptiveCompactionIfNeeded(args) {
119738
119856
  if (toastError instanceof Error)
119739
119857
  return;
119740
119858
  });
119741
- if (error instanceof Error)
119742
- return;
119743
119859
  } finally {
119744
119860
  compactionInProgress.delete(sessionID);
119745
119861
  }
@@ -121758,15 +121874,15 @@ function createRuntimeFallbackHook(ctx, options, factoryOverrides = {}) {
121758
121874
  const messageUpdateHandler = factories.createMessageUpdateHandler(deps, helpers);
121759
121875
  const chatMessageHandler = factories.createChatMessageHandler(deps);
121760
121876
  const firstPromptWatchdog = factories.createFirstPromptWatchdog(deps, helpers);
121761
- let cleanupInterval3 = null;
121877
+ let cleanupInterval4 = null;
121762
121878
  let intervalStarted = false;
121763
121879
  const ensureInterval = () => {
121764
121880
  if (intervalStarted)
121765
121881
  return;
121766
121882
  intervalStarted = true;
121767
- cleanupInterval3 = setInterval(helpers.cleanupStaleSessions, 5 * 60 * 1000);
121768
- if (typeof cleanupInterval3.unref === "function") {
121769
- cleanupInterval3.unref();
121883
+ cleanupInterval4 = setInterval(helpers.cleanupStaleSessions, 5 * 60 * 1000);
121884
+ if (typeof cleanupInterval4.unref === "function") {
121885
+ cleanupInterval4.unref();
121770
121886
  }
121771
121887
  };
121772
121888
  const eventHandler = async ({ event }) => {
@@ -121784,8 +121900,8 @@ function createRuntimeFallbackHook(ctx, options, factoryOverrides = {}) {
121784
121900
  await baseEventHandler({ event });
121785
121901
  };
121786
121902
  const dispose = () => {
121787
- if (cleanupInterval3) {
121788
- clearInterval(cleanupInterval3);
121903
+ if (cleanupInterval4) {
121904
+ clearInterval(cleanupInterval4);
121789
121905
  }
121790
121906
  for (const fallbackTimeout of deps.sessionFallbackTimeouts.values()) {
121791
121907
  clearTimeout(fallbackTimeout);
@@ -121991,7 +122107,6 @@ function toCanonicalPath2(absolutePath) {
121991
122107
  try {
121992
122108
  canonicalPath2 = realpathSync20.native(absolutePath);
121993
122109
  } catch (canonicalPathError) {
121994
- canonicalPathError instanceof Error;
121995
122110
  canonicalPath2 = absolutePath;
121996
122111
  }
121997
122112
  } else {
@@ -123075,11 +123190,11 @@ class Diff {
123075
123190
  return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
123076
123191
  }
123077
123192
  }
123078
- removeEmpty(array8) {
123193
+ removeEmpty(array9) {
123079
123194
  const ret = [];
123080
- for (let i = 0;i < array8.length; i++) {
123081
- if (array8[i]) {
123082
- ret.push(array8[i]);
123195
+ for (let i = 0;i < array9.length; i++) {
123196
+ if (array9[i]) {
123197
+ ret.push(array9[i]);
123083
123198
  }
123084
123199
  }
123085
123200
  return ret;
@@ -124907,16 +125022,41 @@ function formatFsyncSkipWarning(entries) {
124907
125022
  }
124908
125023
 
124909
125024
  // packages/omo-opencode/src/hooks/fsync-skip-warning/index.ts
125025
+ var FSYNC_SKIP_START_TTL_MS = 60000;
125026
+ var startTimesByCallId = new Map;
125027
+ var cleanupInterval4 = null;
125028
+ function pruneStaleStartTimes(now = Date.now()) {
125029
+ for (const [callID, startedAt] of startTimesByCallId) {
125030
+ if (now - startedAt > FSYNC_SKIP_START_TTL_MS) {
125031
+ startTimesByCallId.delete(callID);
125032
+ }
125033
+ }
125034
+ }
125035
+ function ensureCleanupInterval2() {
125036
+ if (cleanupInterval4)
125037
+ return;
125038
+ cleanupInterval4 = setInterval(() => pruneStaleStartTimes(), FSYNC_SKIP_START_TTL_MS);
125039
+ if (typeof cleanupInterval4 === "object" && "unref" in cleanupInterval4) {
125040
+ cleanupInterval4.unref();
125041
+ }
125042
+ }
125043
+ function stopFsyncSkipWarningCleanup() {
125044
+ startTimesByCallId.clear();
125045
+ if (!cleanupInterval4)
125046
+ return;
125047
+ clearInterval(cleanupInterval4);
125048
+ cleanupInterval4 = null;
125049
+ }
124910
125050
  function createFsyncSkipWarningHook() {
124911
- const startTimesByCallId = new Map;
124912
125051
  const toolExecuteBefore = async (input, _output) => {
125052
+ ensureCleanupInterval2();
124913
125053
  startTimesByCallId.set(input.callID, Date.now());
124914
125054
  };
124915
125055
  const toolExecuteAfter = async (input, output) => {
124916
- if (typeof output.output !== "string")
124917
- return;
124918
125056
  const startTimestamp = startTimesByCallId.get(input.callID) ?? 0;
124919
125057
  startTimesByCallId.delete(input.callID);
125058
+ if (typeof output.output !== "string")
125059
+ return;
124920
125060
  const skips = drainSkipsAfter(startTimestamp);
124921
125061
  const warning = formatFsyncSkipWarning(skips);
124922
125062
  if (warning.length === 0)
@@ -124927,7 +125067,8 @@ ${warning}`;
124927
125067
  };
124928
125068
  return {
124929
125069
  "tool.execute.before": toolExecuteBefore,
124930
- "tool.execute.after": toolExecuteAfter
125070
+ "tool.execute.after": toolExecuteAfter,
125071
+ dispose: stopFsyncSkipWarningCleanup
124931
125072
  };
124932
125073
  }
124933
125074
  // packages/omo-opencode/src/hooks/notepad-write-guard/index.ts
@@ -131683,6 +131824,7 @@ function getTerminalSessionError(messages) {
131683
131824
  init_logger2();
131684
131825
  var ACTIVE_SESSION_STATUSES2 = new Set(["busy", "retry", "running"]);
131685
131826
  var CHILD_WAKE_GRACE_MS = 5000;
131827
+ var MAX_NON_ACTIVE_STATUS_STALENESS_POLLS = 10;
131686
131828
  function wait(milliseconds) {
131687
131829
  const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
131688
131830
  const typedArray = new Int32Array(sharedBuffer);
@@ -131702,8 +131844,15 @@ function abortSyncSession(client3, sessionID, reason) {
131702
131844
  function isActiveSessionStatus(status) {
131703
131845
  return status !== undefined && ACTIVE_SESSION_STATUSES2.has(status.type);
131704
131846
  }
131847
+ function hasMessagesAfterAnchor(messages, anchorMessageID, anchorMessageCount) {
131848
+ if (anchorMessageID !== undefined) {
131849
+ const anchorIndex = messages.findIndex((message) => message.info?.id === anchorMessageID);
131850
+ return anchorIndex === -1 || anchorIndex < messages.length - 1;
131851
+ }
131852
+ return anchorMessageCount === undefined || messages.length > anchorMessageCount;
131853
+ }
131705
131854
  async function fetchSessionMessages(client3, sessionID) {
131706
- const messagesResult = await client3.session.messages({ path: { id: sessionID } });
131855
+ const messagesResult = await client3.session.messages({ path: { id: sessionID }, query: { limit: 100 } });
131707
131856
  const rawData = messagesResult?.data ?? messagesResult;
131708
131857
  return Array.isArray(rawData) ? rawData : [];
131709
131858
  }
@@ -131715,6 +131864,9 @@ async function pollSyncSession(ctx, client3, input, timeoutMs) {
131715
131864
  const pollStart = Date.now();
131716
131865
  let inactiveStart = pollStart;
131717
131866
  let pollCount = 0;
131867
+ let nonActivePollsSinceMessageFetch = 0;
131868
+ let lastStatusRevision;
131869
+ let hasFetchedNonActiveMessages = false;
131718
131870
  let timedOut = false;
131719
131871
  let assistantTurnCount = 0;
131720
131872
  let lastSeenAssistantId;
@@ -131762,7 +131914,7 @@ async function pollSyncSession(ctx, client3, input, timeoutMs) {
131762
131914
  }
131763
131915
  }
131764
131916
  if (finalMessages) {
131765
- const hasNewMessages = input.anchorMessageCount === undefined || finalMessages.length > input.anchorMessageCount;
131917
+ const hasNewMessages = hasMessagesAfterAnchor(finalMessages, input.anchorMessageID, input.anchorMessageCount);
131766
131918
  if (hasNewMessages && isSessionComplete(finalMessages)) {
131767
131919
  log2("[task] Abort detected after session already completed", { sessionID: input.sessionID });
131768
131920
  return null;
@@ -131800,6 +131952,15 @@ Session ID: ${input.sessionID}`;
131800
131952
  inactiveStart = Date.now();
131801
131953
  continue;
131802
131954
  }
131955
+ nonActivePollsSinceMessageFetch++;
131956
+ const statusRevision = sessionStatus && (sessionStatus.updatedAt ?? sessionStatus.revision ?? sessionStatus.messageCount);
131957
+ const statusChanged = statusRevision !== undefined && String(statusRevision) !== lastStatusRevision;
131958
+ if (hasFetchedNonActiveMessages && !statusChanged && nonActivePollsSinceMessageFetch < MAX_NON_ACTIVE_STATUS_STALENESS_POLLS) {
131959
+ continue;
131960
+ }
131961
+ lastStatusRevision = statusRevision === undefined ? lastStatusRevision : String(statusRevision);
131962
+ nonActivePollsSinceMessageFetch = 0;
131963
+ hasFetchedNonActiveMessages = true;
131803
131964
  let messages;
131804
131965
  try {
131805
131966
  messages = await fetchSessionMessages(client3, input.sessionID);
@@ -131808,9 +131969,8 @@ Session ID: ${input.sessionID}`;
131808
131969
  log2("[task] Poll messages fetch failed, retrying", { sessionID: input.sessionID, error: errorMessage });
131809
131970
  continue;
131810
131971
  }
131811
- if (input.anchorMessageCount !== undefined && messages.length <= input.anchorMessageCount) {
131972
+ if (!hasMessagesAfterAnchor(messages, input.anchorMessageID, input.anchorMessageCount))
131812
131973
  continue;
131813
- }
131814
131974
  const sessionError = getTerminalSessionError(messages);
131815
131975
  if (sessionError) {
131816
131976
  log2("[task] Poll detected terminal session error", { sessionID: input.sessionID, sessionError });
@@ -131992,6 +132152,54 @@ var syncContinuationDeps = {
131992
132152
 
131993
132153
  // packages/omo-opencode/src/tools/delegate-task/sync-continuation.ts
131994
132154
  init_logger2();
132155
+
132156
+ // packages/omo-opencode/src/features/background-agent/constants.ts
132157
+ var TASK_TTL_MS = 30 * 60 * 1000;
132158
+ var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
132159
+ var MIN_STABILITY_TIME_MS2 = 10 * 1000;
132160
+ var DEFAULT_STALE_TIMEOUT_MS = 2700000;
132161
+ var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 3600000;
132162
+ var DEFAULT_MAX_TOOL_CALLS = 4000;
132163
+ var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
132164
+ var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
132165
+ var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
132166
+ var DEFAULT_SESSION_GONE_TIMEOUT_MS = 60000;
132167
+ var MIN_IDLE_TIME_MS = 5000;
132168
+ var POLLING_INTERVAL_MS = 3000;
132169
+ var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
132170
+
132171
+ // packages/omo-opencode/src/tools/delegate-task/sync-session-cleanup.ts
132172
+ init_logger2();
132173
+ var pendingDeletionTimers = new Map;
132174
+ function cancelSyncSessionDeletion(sessionID) {
132175
+ const timer = pendingDeletionTimers.get(sessionID);
132176
+ if (!timer)
132177
+ return;
132178
+ clearTimeout(timer);
132179
+ pendingDeletionTimers.delete(sessionID);
132180
+ }
132181
+ function scheduleSyncSessionDeletion(client3, sessionID, delayMs = TASK_CLEANUP_DELAY_MS) {
132182
+ cancelSyncSessionDeletion(sessionID);
132183
+ const deleteSession = client3?.session?.delete?.bind(client3.session);
132184
+ if (typeof deleteSession !== "function")
132185
+ return;
132186
+ const timer = setTimeout(() => {
132187
+ pendingDeletionTimers.delete(sessionID);
132188
+ try {
132189
+ deleteSession({ path: { id: sessionID } }).then(() => {
132190
+ handedBackSyncSessions.delete(sessionID);
132191
+ }).catch((error) => {
132192
+ log2("[task] Failed to delete completed sync session:", { sessionID, error: String(error) });
132193
+ });
132194
+ } catch (error) {
132195
+ log2("[task] Failed to schedule completed sync session deletion:", { sessionID, error: String(error) });
132196
+ }
132197
+ }, delayMs);
132198
+ pendingDeletionTimers.set(sessionID, timer);
132199
+ timer.unref();
132200
+ }
132201
+
132202
+ // packages/omo-opencode/src/tools/delegate-task/sync-continuation.ts
131995
132203
  function shouldAttemptPollErrorRecovery(pollError) {
131996
132204
  const trimmed = pollError.trim();
131997
132205
  if (trimmed.length === 0) {
@@ -132022,11 +132230,12 @@ async function resolveResumeContext(client3, continuationID) {
132022
132230
  resumeAgent: info.agent,
132023
132231
  resumeModel: info.model ?? (info.providerID && info.modelID ? { providerID: info.providerID, modelID: info.modelID } : undefined),
132024
132232
  resumeVariant: info.variant,
132025
- anchorMessageCount: messages.length
132233
+ anchorMessageCount: messages.length,
132234
+ anchorMessageID: messages.at(-1)?.info?.id
132026
132235
  };
132027
132236
  }
132028
132237
  }
132029
- return { anchorMessageCount: messages.length };
132238
+ return { anchorMessageCount: messages.length, anchorMessageID: messages.at(-1)?.info?.id };
132030
132239
  } catch (error) {
132031
132240
  if (!(error instanceof Error))
132032
132241
  throw error;
@@ -132047,6 +132256,7 @@ async function executeSyncContinuation(args, ctx, executorCtx, parentContext, de
132047
132256
  if (!continuationID) {
132048
132257
  throw new Error("task_id is required to continue a sync task");
132049
132258
  }
132259
+ cancelSyncSessionDeletion(continuationID);
132050
132260
  const taskId = `resume_sync_${continuationID.slice(0, 8)}`;
132051
132261
  const startTime = new Date;
132052
132262
  if (toastManager) {
@@ -132061,6 +132271,7 @@ async function executeSyncContinuation(args, ctx, executorCtx, parentContext, de
132061
132271
  let resumeModel;
132062
132272
  let resumeVariant;
132063
132273
  let anchorMessageCount;
132274
+ let anchorMessageID;
132064
132275
  let handedBackToParent = false;
132065
132276
  try {
132066
132277
  const resumeContext = await resolveResumeContext(client3, continuationID);
@@ -132068,6 +132279,7 @@ async function executeSyncContinuation(args, ctx, executorCtx, parentContext, de
132068
132279
  resumeModel = resumeContext.resumeModel;
132069
132280
  resumeVariant = resumeContext.resumeVariant;
132070
132281
  anchorMessageCount = resumeContext.anchorMessageCount;
132282
+ anchorMessageID = resumeContext.anchorMessageID;
132071
132283
  const resumeModelForMetadata = resumeModel && resumeVariant !== undefined ? { ...resumeModel, variant: resumeVariant } : resumeModel;
132072
132284
  const syncContMeta = {
132073
132285
  title: args.description,
@@ -132116,6 +132328,7 @@ async function executeSyncContinuation(args, ctx, executorCtx, parentContext, de
132116
132328
  toastManager.removeTask(taskId);
132117
132329
  }
132118
132330
  const errorMessage = promptError instanceof Error ? promptError.message : String(promptError);
132331
+ scheduleSyncSessionDeletion(client3, continuationID);
132119
132332
  return `Failed to send continuation prompt: ${errorMessage}
132120
132333
 
132121
132334
  Task ID: ${continuationID}`;
@@ -132126,7 +132339,8 @@ Task ID: ${continuationID}`;
132126
132339
  agentToUse: resumeAgent ?? "continue",
132127
132340
  toastManager,
132128
132341
  taskId,
132129
- anchorMessageCount
132342
+ anchorMessageCount,
132343
+ anchorMessageID
132130
132344
  }, syncPollTimeoutMs);
132131
132345
  if (pollError && shouldAttemptPollErrorRecovery(pollError)) {
132132
132346
  if (anchorMessageCount === undefined) {
@@ -132188,6 +132402,7 @@ ${buildTaskMetadataBlock({
132188
132402
  });
132189
132403
  }
132190
132404
  }
132405
+ scheduleSyncSessionDeletion(client3, continuationID);
132191
132406
  }
132192
132407
  }
132193
132408
  // packages/omo-opencode/src/tools/delegate-task/cancel-unstable-agent-task.ts
@@ -133151,7 +133366,14 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
133151
133366
  let taskId;
133152
133367
  let syncSessionID;
133153
133368
  let spawnReservation;
133369
+ let concurrencyAcquired = false;
133370
+ const concurrencyModel = categoryModel && typeof categoryModel.providerID === "string" && typeof categoryModel.modelID === "string" ? `${categoryModel.providerID}/${categoryModel.modelID}` : typeof agentToUse === "string" ? agentToUse : "default";
133371
+ const manager = executorCtx?.manager;
133154
133372
  try {
133373
+ if (!subagentSessions.has(parentContext.sessionID) && typeof manager?.acquireSyncSubagentConcurrency === "function") {
133374
+ await manager.acquireSyncSubagentConcurrency(concurrencyModel);
133375
+ concurrencyAcquired = true;
133376
+ }
133155
133377
  const spawn5 = await reserveSyncSubagentSpawn(executorCtx, parentContext);
133156
133378
  spawnReservation = spawn5.reservation;
133157
133379
  const { spawnContext } = spawn5;
@@ -133260,11 +133482,15 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
133260
133482
  if (syncSessionID) {
133261
133483
  cleanupSyncSessionSideEffects(syncSessionID, executorCtx);
133262
133484
  handedBackSyncSessions.add(syncSessionID);
133263
- if (typeof client3.session.abort === "function") {
133485
+ if (typeof client3?.session?.abort === "function") {
133264
133486
  client3.session.abort({ path: { id: syncSessionID } }).catch((error) => {
133265
133487
  log2(`[task] Failed to abort completed sync session:`, error);
133266
133488
  });
133267
133489
  }
133490
+ scheduleSyncSessionDeletion(client3, syncSessionID);
133491
+ }
133492
+ if (concurrencyAcquired && typeof manager?.releaseSyncSubagentConcurrency === "function") {
133493
+ manager.releaseSyncSubagentConcurrency(concurrencyModel);
133268
133494
  }
133269
133495
  }
133270
133496
  }
@@ -137105,21 +137331,6 @@ class ConcurrencyManager {
137105
137331
  }
137106
137332
  }
137107
137333
 
137108
- // packages/omo-opencode/src/features/background-agent/constants.ts
137109
- var TASK_TTL_MS = 30 * 60 * 1000;
137110
- var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
137111
- var MIN_STABILITY_TIME_MS2 = 10 * 1000;
137112
- var DEFAULT_STALE_TIMEOUT_MS = 2700000;
137113
- var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 3600000;
137114
- var DEFAULT_MAX_TOOL_CALLS = 4000;
137115
- var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
137116
- var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
137117
- var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
137118
- var DEFAULT_SESSION_GONE_TIMEOUT_MS = 60000;
137119
- var MIN_IDLE_TIME_MS = 5000;
137120
- var POLLING_INTERVAL_MS = 3000;
137121
- var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
137122
-
137123
137334
  // packages/omo-opencode/src/features/background-agent/duration-formatter.ts
137124
137335
  function formatDuration3(start, end) {
137125
137336
  const duration = (end ?? new Date).getTime() - start.getTime();
@@ -137557,9 +137768,6 @@ class ParentWakeDispatchedTracker {
137557
137768
  getWake(sessionID) {
137558
137769
  return this.dispatchedParentWakes.get(sessionID);
137559
137770
  }
137560
- hasWake(sessionID) {
137561
- return this.dispatchedParentWakes.has(sessionID);
137562
- }
137563
137771
  clearWake(sessionID) {
137564
137772
  const timer = this.dispatchedParentWakeTimers.get(sessionID);
137565
137773
  if (timer) {
@@ -139108,31 +139316,29 @@ function invokeTmuxSessionCreatedCallback(invocation) {
139108
139316
 
139109
139317
  // packages/omo-opencode/src/features/background-agent/subagent-spawn-limits.ts
139110
139318
  var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
139319
+ var DEFAULT_MAX_LIVE_DESCENDANTS_PER_ROOT = 24;
139111
139320
  function getMaxSubagentDepth(config3) {
139112
139321
  return config3?.maxDepth ?? DEFAULT_MAX_SUBAGENT_DEPTH;
139113
139322
  }
139323
+ function getMaxLiveDescendantsPerRoot(config3) {
139324
+ return config3?.maxLiveDescendantsPerRoot ?? DEFAULT_MAX_LIVE_DESCENDANTS_PER_ROOT;
139325
+ }
139114
139326
  async function resolveSubagentSpawnContext(client3, parentSessionID, directory) {
139115
139327
  const visitedSessionIDs = new Set;
139116
139328
  let rootSessionID = parentSessionID;
139117
139329
  let currentSessionID = parentSessionID;
139118
139330
  let parentDepth = 0;
139119
139331
  while (true) {
139120
- if (visitedSessionIDs.has(currentSessionID)) {
139332
+ if (visitedSessionIDs.has(currentSessionID))
139121
139333
  throw new Error(`Detected a session parent cycle while resolving ${parentSessionID}`);
139122
- }
139123
139334
  visitedSessionIDs.add(currentSessionID);
139124
139335
  let nextParentSessionID;
139125
139336
  try {
139126
- const response = await client3.session.get({
139127
- path: { id: currentSessionID },
139128
- ...directory ? { query: { directory } } : {}
139129
- });
139130
- if (response.error) {
139337
+ const response = await client3.session.get({ path: { id: currentSessionID }, ...directory ? { query: { directory } } : {} });
139338
+ if (response.error)
139131
139339
  throw new Error(String(response.error));
139132
- }
139133
- if (!response.data) {
139340
+ if (!response.data)
139134
139341
  throw new Error("No session data returned");
139135
- }
139136
139342
  nextParentSessionID = response.data.parentID;
139137
139343
  } catch (error) {
139138
139344
  const reason = error instanceof Error ? error.message : String(error);
@@ -139145,15 +139351,13 @@ async function resolveSubagentSpawnContext(client3, parentSessionID, directory)
139145
139351
  currentSessionID = nextParentSessionID;
139146
139352
  parentDepth += 1;
139147
139353
  }
139148
- return {
139149
- rootSessionID,
139150
- parentDepth,
139151
- childDepth: parentDepth + 1
139152
- };
139354
+ return { rootSessionID, parentDepth, childDepth: parentDepth + 1 };
139153
139355
  }
139154
139356
  function createSubagentDepthLimitError(input) {
139155
- const { childDepth, maxDepth, parentSessionID, rootSessionID } = input;
139156
- return new Error(`Subagent spawn blocked: child depth ${childDepth} exceeds background_task.maxDepth=${maxDepth}. Parent session: ${parentSessionID}. Root session: ${rootSessionID}. Continue in an existing subagent session instead of spawning another.`);
139357
+ return new Error(`Subagent spawn blocked: child depth ${input.childDepth} exceeds background_task.maxDepth=${input.maxDepth}. Parent session: ${input.parentSessionID}. Root session: ${input.rootSessionID}. Continue in an existing subagent session instead of spawning another.`);
139358
+ }
139359
+ function createSubagentDescendantLimitError(input) {
139360
+ return new Error(`Subagent spawn blocked: root session ${input.rootSessionID} already has ${input.descendantCount} live descendants, reaching background_task.maxLiveDescendantsPerRoot=${input.maxDescendants}. Parent session: ${input.parentSessionID}.`);
139157
139361
  }
139158
139362
 
139159
139363
  // packages/omo-opencode/src/features/background-agent/task-history.ts
@@ -139855,6 +140059,16 @@ class BackgroundManager {
139855
140059
  }
139856
140060
  async reserveSubagentSpawn(parentSessionID) {
139857
140061
  const spawnContext = await this.assertCanSpawn(parentSessionID);
140062
+ const maxDescendants = getMaxLiveDescendantsPerRoot(this.config);
140063
+ const currentCount = this.rootDescendantCounts.get(spawnContext.rootSessionID) ?? 0;
140064
+ if (maxDescendants !== 0 && currentCount >= maxDescendants) {
140065
+ throw createSubagentDescendantLimitError({
140066
+ descendantCount: currentCount,
140067
+ maxDescendants,
140068
+ parentSessionID,
140069
+ rootSessionID: spawnContext.rootSessionID
140070
+ });
140071
+ }
139858
140072
  const descendantCount = this.registerRootDescendant(spawnContext.rootSessionID);
139859
140073
  let settled = false;
139860
140074
  return {
@@ -139872,6 +140086,12 @@ class BackgroundManager {
139872
140086
  }
139873
140087
  };
139874
140088
  }
140089
+ async acquireSyncSubagentConcurrency(model, taskId) {
140090
+ await this.concurrencyManager.acquire(model, taskId);
140091
+ }
140092
+ releaseSyncSubagentConcurrency(model) {
140093
+ this.concurrencyManager.release(model);
140094
+ }
139875
140095
  registerRootDescendant(rootSessionID) {
139876
140096
  const nextCount = (this.rootDescendantCounts.get(rootSessionID) ?? 0) + 1;
139877
140097
  this.rootDescendantCounts.set(rootSessionID, nextCount);
@@ -140775,18 +140995,6 @@ The fallback retry session is now created and can be inspected directly.
140775
140995
  clearSessionTodoObservation(sessionID) {
140776
140996
  this.observedIncompleteTodosBySession.delete(sessionID);
140777
140997
  }
140778
- messageUpdatedInfoHasParentWakeOutput(info, role) {
140779
- if (role === "tool") {
140780
- return true;
140781
- }
140782
- if (role !== "assistant") {
140783
- return false;
140784
- }
140785
- if (info.error) {
140786
- return false;
140787
- }
140788
- return !isEmptyNoProgressAssistantTurnInfo(info);
140789
- }
140790
140998
  shouldHoldDispatchedParentWakeForTextDelta(eventType, partInfo, sessionID, wake) {
140791
140999
  if (eventType !== "message.part.delta") {
140792
141000
  return false;
@@ -141414,7 +141622,7 @@ The task was re-queued on a fallback model after a retryable failure.
141414
141622
  clearTimeout(existingTimer);
141415
141623
  this.completionTimers.delete(taskId);
141416
141624
  }
141417
- const timer = setTimeout(() => {
141625
+ const timer = setTimeout(async () => {
141418
141626
  this.completionTimers.delete(taskId);
141419
141627
  const task2 = this.tasks.get(taskId);
141420
141628
  if (!task2)
@@ -141436,6 +141644,12 @@ The task was re-queued on a fallback model after a retryable failure.
141436
141644
  subagentSessions.delete(task2.sessionId);
141437
141645
  clearDelegatedChildSessionBootstrap(task2.sessionId);
141438
141646
  SessionCategoryRegistry.remove(task2.sessionId);
141647
+ const deleteSession = this.client.session.delete?.bind(this.client.session);
141648
+ if (typeof deleteSession === "function") {
141649
+ await deleteSession({ path: { id: task2.sessionId } }).catch((error) => {
141650
+ log2("[background-agent] Failed to delete completed subagent session:", { sessionID: task2.sessionId, error: String(error) });
141651
+ });
141652
+ }
141439
141653
  }
141440
141654
  log2("[background-agent] Removed completed task from memory:", taskId);
141441
141655
  }, this.config?.taskCleanupDelayMs ?? TASK_CLEANUP_DELAY_MS);
@@ -149329,6 +149543,7 @@ class TuiStateMirror {
149329
149543
  this.heartbeatID = setInterval(() => {
149330
149544
  this.flush();
149331
149545
  }, HEARTBEAT_MS);
149546
+ this.heartbeatID.unref?.();
149332
149547
  }
149333
149548
  stop() {
149334
149549
  this.stopped = true;
@@ -173235,7 +173450,7 @@ async function createTools(args) {
173235
173450
  }
173236
173451
  // packages/omo-opencode/src/plugin-dispose.ts
173237
173452
  function createPluginDispose(args) {
173238
- const { backgroundManager, skillMcpManager, disposeHooks } = args;
173453
+ const { backgroundManager, skillMcpManager, tuiStateMirror, disposeHooks } = args;
173239
173454
  let disposePromise = null;
173240
173455
  return async () => {
173241
173456
  if (disposePromise) {
@@ -173243,6 +173458,11 @@ function createPluginDispose(args) {
173243
173458
  return;
173244
173459
  }
173245
173460
  disposePromise = (async () => {
173461
+ try {
173462
+ tuiStateMirror?.stop();
173463
+ } catch (error) {
173464
+ log2("[plugin-dispose] tuiStateMirror.stop() error:", error);
173465
+ }
173246
173466
  try {
173247
173467
  await backgroundManager.shutdown();
173248
173468
  } catch (error) {
@@ -174874,6 +175094,8 @@ async function handleSessionDeletedEvent(args) {
174874
175094
  await args.managers.monitorManager?.stopSessionMonitors(sessionID);
174875
175095
  const wasSyncSubagentSession = syncSubagentSessions.has(sessionID);
174876
175096
  clearSessionAgent(sessionID);
175097
+ handedBackSyncSessions.delete(sessionID);
175098
+ subagentSessions.delete(sessionID);
174877
175099
  args.clearModelFallbackSession(sessionID);
174878
175100
  resetMessageCursor(sessionID);
174879
175101
  clearBackgroundOutputConsumptionsForParentSession(sessionID);
@@ -174885,8 +175107,6 @@ async function handleSessionDeletedEvent(args) {
174885
175107
  if (!isBtwSideSession) {
174886
175108
  await dispatchOpenClawSessionEvent({ ...args, rawEvent: "session.deleted", sessionID });
174887
175109
  }
174888
- if (wasSyncSubagentSession)
174889
- subagentSessions.delete(sessionID);
174890
175110
  deleteSessionTools(sessionID);
174891
175111
  await args.managers.skillMcpManager.disconnectSession(sessionID);
174892
175112
  if (args.tmuxIntegrationEnabled && !isBtwSideSession) {
@@ -184121,6 +184341,7 @@ function createPluginModule(overrides2 = {}) {
184121
184341
  const dispose = createPluginDispose({
184122
184342
  backgroundManager: managers.backgroundManager,
184123
184343
  skillMcpManager: managers.skillMcpManager,
184344
+ tuiStateMirror: managers.tuiStateMirror,
184124
184345
  disposeHooks: hooks2.disposeHooks
184125
184346
  });
184126
184347
  const pluginHooks = {