oh-my-opencode 5.1.30 → 5.1.33

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 (90) hide show
  1. package/dist/cli/index.js +25 -19
  2. package/dist/cli/service-auth/commands.d.ts +11 -0
  3. package/dist/cli-node/index.js +25 -19
  4. package/dist/features/background-agent/concurrency.d.ts +6 -0
  5. package/dist/features/background-agent/manager.d.ts +2 -0
  6. package/dist/features/background-agent/task-history.d.ts +1 -0
  7. package/dist/features/background-agent/task-poller.d.ts +3 -0
  8. package/dist/features/background-agent/types.d.ts +2 -0
  9. package/dist/features/team-mode/tools/lifecycle-inline-spec.d.ts +1 -0
  10. package/dist/hooks/keyword-detector/constants.d.ts +1 -0
  11. package/dist/index.js +319 -66
  12. package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
  13. package/dist/skills/ulw-execute/SKILL.md +10 -7
  14. package/dist/skills/ulw-research/SKILL.md +4 -4
  15. package/dist/tools/delegate-task/executor-types.d.ts +1 -0
  16. package/dist/tools/delegate-task/sync-session-creator.d.ts +1 -0
  17. package/dist/tools/delegate-task/types.d.ts +2 -0
  18. package/dist/tools/session-manager/session-error.d.ts +7 -0
  19. package/dist/tui.js +18 -16
  20. package/package.json +14 -14
  21. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  22. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -2
  23. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  24. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  26. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  28. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  30. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +7 -4
  32. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  33. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  34. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  36. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  38. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +3 -3
  40. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +18 -15
  43. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  44. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  46. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  47. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  48. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +18 -15
  49. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  50. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -2
  52. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
  53. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  74. package/packages/omo-codex/plugin/package-lock.json +12 -12
  75. package/packages/omo-codex/plugin/package.json +1 -1
  76. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +2 -2
  77. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  78. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +7 -4
  79. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +3 -3
  80. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +18 -15
  81. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +10 -7
  82. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -2
  83. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
  84. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +4 -4
  85. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -2
  86. package/packages/omo-codex/scripts/install-dist/install-local.mjs +6 -4
  87. package/packages/prompts-core/prompts/ultrawork/codex.md +18 -15
  88. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  89. package/packages/shared-skills/skills/ulw-execute/SKILL.md +10 -7
  90. package/packages/shared-skills/skills/ulw-research/SKILL.md +4 -4
package/dist/index.js CHANGED
@@ -20393,7 +20393,8 @@ var OmoMemoryNudgeSchema = z8.object({
20393
20393
  }).strict();
20394
20394
  var OmoMemoryFactsSchema = z8.object({
20395
20395
  enabled: z8.boolean().default(true),
20396
- debounce_settles: z8.number().int().min(1).default(4)
20396
+ debounce_settles: z8.number().int().min(1).default(4),
20397
+ model: z8.string().min(1).optional()
20397
20398
  }).strict();
20398
20399
  var OmoMemoryDreamSchema = z8.object({
20399
20400
  enabled: z8.boolean().default(true),
@@ -20455,7 +20456,8 @@ var OmoMemoryNudgeLayerSchema = z8.object({
20455
20456
  }).strict();
20456
20457
  var OmoMemoryFactsLayerSchema = z8.object({
20457
20458
  enabled: z8.boolean().optional(),
20458
- debounce_settles: z8.number().int().min(1).optional()
20459
+ debounce_settles: z8.number().int().min(1).optional(),
20460
+ model: z8.string().min(1).optional()
20459
20461
  }).strict();
20460
20462
  var OmoMemoryDreamLayerSchema = z8.object({
20461
20463
  enabled: z8.boolean().optional(),
@@ -24643,6 +24645,9 @@ function isRuntimeFallbackRetryableError(error, retryOnErrors, options = {}) {
24643
24645
  const errorType = classifyRuntimeFallbackError(error);
24644
24646
  if (errorType === "abort" || errorType === "context_overflow")
24645
24647
  return false;
24648
+ if (getRuntimeFallbackErrorName(error)?.toLowerCase().replace(/[_-]/g, "") === "contentfiltererror") {
24649
+ return true;
24650
+ }
24646
24651
  if (errorType === "missing_api_key" || errorType === "model_not_found" || errorType === "quota_exceeded") {
24647
24652
  return true;
24648
24653
  }
@@ -99132,7 +99137,7 @@ function getCachedVersion(options = {}) {
99132
99137
  // package.json
99133
99138
  var package_default = {
99134
99139
  name: "oh-my-opencode",
99135
- version: "5.1.30",
99140
+ version: "5.1.33",
99136
99141
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
99137
99142
  main: "./dist/index.js",
99138
99143
  types: "dist/index.d.ts",
@@ -99309,7 +99314,7 @@ var package_default = {
99309
99314
  },
99310
99315
  devDependencies: {
99311
99316
  "@clack/prompts": "^1.8.1",
99312
- "@code-yeongyu/senpi": "2026.10.11",
99317
+ "@code-yeongyu/senpi": "2026.10.13",
99313
99318
  "@modelcontextprotocol/sdk": "^1.30.0",
99314
99319
  "@oh-my-opencode/agents-md-core": "workspace:*",
99315
99320
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
@@ -99368,18 +99373,18 @@ var package_default = {
99368
99373
  typescript: "^7.0.2"
99369
99374
  },
99370
99375
  optionalDependencies: {
99371
- "oh-my-opencode-darwin-arm64": "5.1.30",
99372
- "oh-my-opencode-darwin-x64": "5.1.30",
99373
- "oh-my-opencode-darwin-x64-baseline": "5.1.30",
99374
- "oh-my-opencode-linux-arm64": "5.1.30",
99375
- "oh-my-opencode-linux-arm64-musl": "5.1.30",
99376
- "oh-my-opencode-linux-x64": "5.1.30",
99377
- "oh-my-opencode-linux-x64-baseline": "5.1.30",
99378
- "oh-my-opencode-linux-x64-musl": "5.1.30",
99379
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.30",
99380
- "oh-my-opencode-windows-arm64": "5.1.30",
99381
- "oh-my-opencode-windows-x64": "5.1.30",
99382
- "oh-my-opencode-windows-x64-baseline": "5.1.30"
99376
+ "oh-my-opencode-darwin-arm64": "5.1.33",
99377
+ "oh-my-opencode-darwin-x64": "5.1.33",
99378
+ "oh-my-opencode-darwin-x64-baseline": "5.1.33",
99379
+ "oh-my-opencode-linux-arm64": "5.1.33",
99380
+ "oh-my-opencode-linux-arm64-musl": "5.1.33",
99381
+ "oh-my-opencode-linux-x64": "5.1.33",
99382
+ "oh-my-opencode-linux-x64-baseline": "5.1.33",
99383
+ "oh-my-opencode-linux-x64-musl": "5.1.33",
99384
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.33",
99385
+ "oh-my-opencode-windows-arm64": "5.1.33",
99386
+ "oh-my-opencode-windows-x64": "5.1.33",
99387
+ "oh-my-opencode-windows-x64-baseline": "5.1.33"
99383
99388
  },
99384
99389
  overrides: {
99385
99390
  hono: "^4.13.8",
@@ -105362,7 +105367,13 @@ task(category="quick", load_skills=["git-master"], run_in_background=true)
105362
105367
 
105363
105368
  ---
105364
105369
 
105370
+ ## Task resources
105371
+ Before the first task command, create and record one \`RUN_TMP=$(mktemp -d)\` per task. Export \`TMPDIR="$RUN_TMP" NODE_COMPILE_CACHE="$RUN_TMP/node-compile-cache" BUN_RUNTIME_TRANSPILER_CACHE_PATH="$RUN_TMP/bun-transpiler"\` for every command; pass the same environment explicitly to children that do not inherit it. Give independent child tasks their own run roots.
105372
+ At task end, after owned processes exit, remove only owned directories, including runtime caches. Never wipe shared caches; retain worktrees still needed by open PRs and any parent directory containing them.
105373
+ ALWAYS add a cleanup todo AT CREATION for each worktree, cache directory (including the run root), and QA resource (PID, tmux session, browser context, port, container, script or temp path); complete it only after a read-back teardown receipt.
105374
+
105365
105375
  ## EXECUTION RULES
105376
+
105366
105377
  - **TODO format**: \`path: <action> for <scenario-id> \u2014 verify by <check>\` encoding WHERE / WHY (which scenario it advances) / HOW / VERIFY. Exactly ONE in_progress at a time. Mark completed IMMEDIATELY \u2014 never batch.
105367
105378
  - GOOD pair (ordered): \`test/module.test: read the validateEmail cases for S2 - verify by noting intent / coverage / pass in the notepad\` \u2192 \`src/module: Implement validateEmail() for S2 - verify by curl 400 body + module.test green\`
105368
105379
  - BAD: "Implement feature" / "Fix bug" / "Add tests later" \u2192 rewrite.
@@ -105462,7 +105473,7 @@ The real-surface artifact is always required. A new test only where the reposito
105462
105473
 
105463
105474
  **NAME THE EXACT TOOL + EXACT INVOCATION** for every scenario \u2014 the literal \`curl ...\`, \`tmux send-keys ...\`, \`page.click(...)\` with concrete inputs and the binary observable. "run it" / "open the page" is not a scenario.
105464
105475
 
105465
- **CLEANUP IS PART OF QA \u2014 TRACK IT AS TODOS.** The moment a QA scenario spawns any resource, add a teardown todo for it (QA scripts, tmux assets, browser contexts, PIDs, ports, containers, temp dirs). Execute every teardown todo and capture the receipt before declaring done. A leftover process / tmux session / browser context / bound port / temp dir = NOT done.
105476
+ **CLEANUP IS PART OF THE TASK \u2014 TRACK IT AS TODOS.** Tear down QA scripts, tmux assets, browser contexts, PIDs, ports, containers and QA temp paths inside \`$RUN_TMP\` before recording QA; leftover QA resources mean NOT done. Remove the run root and eligible worktrees at task end under Task resources.
105466
105477
  </MANUAL_QA_MANDATE>
105467
105478
 
105468
105479
  ### Test Decision (every production code change)
@@ -105710,7 +105721,13 @@ task(subagent_type="plan", load_skills=[], run_in_background=false, prompt="<gat
105710
105721
 
105711
105722
  ---
105712
105723
 
105724
+ ## Task resources
105725
+ Before the first task command, create and record one \`RUN_TMP=$(mktemp -d)\` per task. Export \`TMPDIR="$RUN_TMP" NODE_COMPILE_CACHE="$RUN_TMP/node-compile-cache" BUN_RUNTIME_TRANSPILER_CACHE_PATH="$RUN_TMP/bun-transpiler"\` for every command; pass the same environment explicitly to children that do not inherit it. Give independent child tasks their own run roots.
105726
+ At task end, after owned processes exit, remove only owned directories, including runtime caches. Never wipe shared caches; retain worktrees still needed by open PRs and any parent directory containing them.
105727
+ ALWAYS add a cleanup todo AT CREATION for each worktree, cache directory (including the run root), and QA resource (PID, tmux session, browser context, port, container, script or temp path); complete it only after a read-back teardown receipt.
105728
+
105713
105729
  ## EXECUTION RULES
105730
+
105714
105731
  - **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each.
105715
105732
  - **PARALLEL**: Fire independent agent calls simultaneously via task(run_in_background=true) - NEVER wait sequentially.
105716
105733
  - **BACKGROUND FIRST**: Use task for exploration/research agents (10+ concurrent if needed).
@@ -105810,7 +105827,7 @@ Trigger if user said "\uC5C4\uBC00"/"strictly"/"rigorously"/"properly review", o
105810
105827
  | Adds a new tool/hook/feature | Test it end-to-end in a real scenario. |
105811
105828
  | Modifies config handling | Load the config. Verify it parses correctly. |
105812
105829
 
105813
- **NAME THE EXACT TOOL + EXACT INVOCATION** per scenario \u2014 the literal \`curl\` / \`send-keys\` / \`page.click\` with inputs and the binary observable. **REGISTER EVERY QA-SPAWNED RESOURCE TEARDOWN AS ITS OWN TODO** (scripts, tmux assets, browser contexts, PIDs, ports, temp dirs), execute it, capture the receipt. A leftover process / tmux session / browser context = NOT done.
105830
+ **NAME THE EXACT TOOL + INVOCATION** per scenario. Tear down QA scripts, tmux assets, browser contexts, PIDs, ports, containers and QA temp paths inside \`$RUN_TMP\` before recording QA; leftover QA resources mean NOT done. Remove the run root and eligible worktrees at task end under Task resources.
105814
105831
 
105815
105832
  **UNACCEPTABLE (WILL BE REJECTED):**
105816
105833
  - "This should work" - DID YOU RUN IT? NO? THEN RUN IT.
@@ -105995,6 +106012,11 @@ If a verification command is unavailable or not applicable, state the exact reas
105995
106012
 
105996
106013
  When a \`create_goal\` tool exists, check \`get_goal\` first (continue a matching active goal; never duplicate), then register the run's goal before implementation with exactly \`objective\`, written outcome-first: the concrete outcome that will be true (never an activity), the named deliverable surfaces, the scenario contract as success criteria that can fail, scope bounds, and one WHEN TO STOP line naming the observable end state. Never invent a budget or deadline the user did not state. Without the tool, record the same contract in your working notes and treat it as binding.
105997
106014
 
106015
+ ## Task resources
106016
+ Before the first task command, create and record one \`RUN_TMP=$(mktemp -d)\` per task. Export \`TMPDIR="$RUN_TMP" NODE_COMPILE_CACHE="$RUN_TMP/node-compile-cache" BUN_RUNTIME_TRANSPILER_CACHE_PATH="$RUN_TMP/bun-transpiler"\` for every command; pass the same environment explicitly to children that do not inherit it. Give independent child tasks their own run roots.
106017
+ At task end, after owned processes exit, remove only owned directories, including runtime caches. Never wipe shared caches; retain worktrees still needed by open PRs and any parent directory containing them.
106018
+ ALWAYS add a cleanup todo AT CREATION for each worktree, cache directory (including the run root), and QA resource (PID, tmux session, browser context, port, container, script or temp path); complete it only after a read-back teardown receipt.
106019
+
105998
106020
  ## TODO DISCIPLINE
105999
106021
 
106000
106022
  Track every multi-step task in a live todo list: one atomic item per action with its verification, exactly one item in progress, status updated the instant it changes, newly discovered work added immediately. Never batch completions.
@@ -106038,7 +106060,7 @@ Tests are necessary and insufficient. Exercise the real surface.
106038
106060
  | Prompt or mode | Verify the prompt loads or the registry resolves it. |
106039
106061
  | Build output | Run build and verify exit code 0. |
106040
106062
 
106041
- If QA starts a server, browser, tmux session, port, temp dir, or background process, clean it up and record the cleanup.
106063
+ Tear down QA scripts, tmux assets, browser contexts, PIDs, ports, containers and QA temp paths inside \`$RUN_TMP\` before recording QA; leftover QA resources mean NOT done. Remove the run root and eligible worktrees at task end under Task resources.
106042
106064
 
106043
106065
  ## REVIEWER GATE
106044
106066
 
@@ -106193,6 +106215,11 @@ At start, run \`NOTE=$(mktemp -t ulw-$(date +%Y%m%d-%H%M%S).XXXXXX.md)\` and ech
106193
106215
 
106194
106216
  When a \`create_goal\` tool exists, check \`get_goal\` first (continue a matching active goal; never duplicate), then register the run's goal before implementation with exactly \`objective\`, outcome-first: the concrete outcome that will be true (never an activity), named deliverable surfaces, the scenario contract as criteria that can fail, scope bounds, and the WHEN TO STOP line. No invented budgets or deadlines. No tool \u2192 record the same contract in the notepad and treat it as binding.
106195
106217
 
106218
+ ## Task resources
106219
+ Before the first task command, create and record one \`RUN_TMP=$(mktemp -d)\` per task. Export \`TMPDIR="$RUN_TMP" NODE_COMPILE_CACHE="$RUN_TMP/node-compile-cache" BUN_RUNTIME_TRANSPILER_CACHE_PATH="$RUN_TMP/bun-transpiler"\` for every command; pass the same environment explicitly to children that do not inherit it. Give independent child tasks their own run roots.
106220
+ At task end, after owned processes exit, remove only owned directories, including runtime caches. Never wipe shared caches; retain worktrees still needed by open PRs and any parent directory containing them.
106221
+ ALWAYS add a cleanup todo AT CREATION for each worktree, cache directory (including the run root), and QA resource (PID, tmux session, browser context, port, container, script or temp path); complete it only after a read-back teardown receipt.
106222
+
106196
106223
  ## TODO DISCIPLINE
106197
106224
 
106198
106225
  Maintain a live todo list for every multi-step task: one atomic item per action (\`path: <action> for <scenario> \u2014 verify by <check>\`), exactly one in_progress, transitions marked the instant they happen, discovered work inserted immediately. Never batch completions.
@@ -106241,7 +106268,7 @@ lsp_diagnostics catches type errors only. Logic bugs, missing behavior, broken f
106241
106268
  | Adds tool/hook/feature | Test end-to-end in a real scenario. |
106242
106269
  | Modifies config handling | Load config. Verify parsed shape. |
106243
106270
 
106244
- Name the exact tool + exact invocation per scenario (literal \`curl\` / \`send-keys\` / \`page.click\` + inputs + binary observable). Register every QA-spawned resource teardown as its own todo (scripts, tmux, browser contexts, PIDs, ports, temp dirs), execute it, capture the receipt. "This should work" / "tests pass" / "lsp clean" / a leftover process are NOT done \u2014 the surface artifact + clean teardown are.
106271
+ Name the exact tool and invocation per scenario. Tear down QA scripts, tmux assets, browser contexts, PIDs, ports, containers and QA temp paths inside \`$RUN_TMP\` before recording QA; leftover QA resources mean NOT done. Remove the run root and eligible worktrees at task end under Task resources.
106245
106272
  </MANUAL_QA_MANDATE>
106246
106273
 
106247
106274
  ## REVIEWER GATE (triggered)
@@ -106431,7 +106458,8 @@ var HYPERPLAN_MESSAGE = HYPERPLAN_MODE_PROMPT;
106431
106458
  // packages/omo-opencode/src/hooks/keyword-detector/constants.ts
106432
106459
  var CODE_BLOCK_PATTERN2 = /```[\s\S]*?```/g;
106433
106460
  var INLINE_CODE_PATTERN2 = /`[^`]+`/g;
106434
- var HYPERPLAN_ULTRAWORK_PATTERN = /\b(?:hpp|hyperplan)\s+(?:ulw|ultrawork)\b|\b(?:ulw|ultrawork)\s+(?:hpp|hyperplan)\b/i;
106461
+ var ULTRAWORK_PATTERN = /\b(?:ultrawork|ulw(?!-\w))\b/i;
106462
+ var HYPERPLAN_ULTRAWORK_PATTERN = /\b(?:hpp|hyperplan)\s+(?:ultrawork|ulw(?!-\w))\b|\b(?:ultrawork|ulw(?!-\w))\s+(?:hpp|hyperplan)\b/i;
106435
106463
  var HYPERPLAN_ULTRAWORK_BANNER = `<hyperplan-ultrawork-mode>
106436
106464
  **MANDATORY**: Say "HYPERPLAN ULTRAWORK MODE ENABLED!" exactly once as your first response. Do NOT say the standalone "ULTRAWORK MODE ENABLED!" or "HYPERPLAN MODE ENABLED!" banners.
106437
106465
 
@@ -106445,7 +106473,7 @@ ${getUltraworkMessage(agentName, modelID)}`;
106445
106473
  var KEYWORD_DETECTORS = [
106446
106474
  {
106447
106475
  type: "ultrawork",
106448
- pattern: /\b(ultrawork|ulw)\b/i,
106476
+ pattern: ULTRAWORK_PATTERN,
106449
106477
  message: getUltraworkMessage
106450
106478
  },
106451
106479
  {
@@ -121776,6 +121804,8 @@ function createMessageUpdateHandler(deps, helpers) {
121776
121804
  return;
121777
121805
  }
121778
121806
  if (sessionID && role === "assistant" && error) {
121807
+ const isContentFilterError = extractErrorName(error)?.toLowerCase().replace(/[_-]/g, "") === "contentfiltererror";
121808
+ const shouldOverrideRetry = Boolean(retrySignal && timeoutEnabled) || isContentFilterError;
121779
121809
  let state = sessionStates.get(sessionID);
121780
121810
  const pendingFallbackModel = state?.pendingFallbackModel;
121781
121811
  const wasAwaitingFallbackResult = sessionAwaitingFallbackResult.has(sessionID);
@@ -121790,12 +121820,12 @@ function createMessageUpdateHandler(deps, helpers) {
121790
121820
  if (wasAwaitingFallbackResult) {
121791
121821
  sessionAwaitingFallbackResult.delete(sessionID);
121792
121822
  }
121793
- if (sessionRetryInFlight.has(sessionID) && !retrySignal) {
121823
+ if (sessionRetryInFlight.has(sessionID) && !retrySignal && !isContentFilterError) {
121794
121824
  log2(`[${HOOK_NAME13}] message.updated fallback skipped (retry in flight)`, { sessionID });
121795
121825
  return;
121796
121826
  }
121797
- if (retrySignal && timeoutEnabled && (sessionRetryInFlight.has(sessionID) || wasAwaitingFallbackResult)) {
121798
- log2(`[${HOOK_NAME13}] Overriding active retry due to provider auto-retry signal`, {
121827
+ if (shouldOverrideRetry && (sessionRetryInFlight.has(sessionID) || wasAwaitingFallbackResult)) {
121828
+ log2(`[${HOOK_NAME13}] Overriding active retry due to provider retry signal or content-filter error`, {
121799
121829
  sessionID,
121800
121830
  model
121801
121831
  });
@@ -121868,8 +121898,8 @@ function createMessageUpdateHandler(deps, helpers) {
121868
121898
  } else {
121869
121899
  sessionLastAccess.set(sessionID, Date.now());
121870
121900
  if (state.pendingFallbackModel) {
121871
- if (retrySignal && timeoutEnabled) {
121872
- log2(`[${HOOK_NAME13}] Clearing pending fallback due to provider auto-retry signal`, {
121901
+ if (shouldOverrideRetry) {
121902
+ log2(`[${HOOK_NAME13}] Clearing pending fallback due to provider retry signal or content-filter error`, {
121873
121903
  sessionID,
121874
121904
  pendingFallbackModel: state.pendingFallbackModel
121875
121905
  });
@@ -131918,6 +131948,58 @@ function isSessionSdkUnavailableError(value) {
131918
131948
  return SDK_UNAVAILABLE_PATTERNS.some((pattern) => haystack.includes(pattern));
131919
131949
  }
131920
131950
 
131951
+ // packages/omo-opencode/src/tools/session-manager/session-error.ts
131952
+ function textField(record, key) {
131953
+ const value = record[key];
131954
+ return typeof value === "string" && value.length > 0 ? value : undefined;
131955
+ }
131956
+ function describeSessionError(error) {
131957
+ if (error instanceof Error)
131958
+ return error.message;
131959
+ if (typeof error === "string")
131960
+ return error;
131961
+ if (!error || typeof error !== "object")
131962
+ return String(error);
131963
+ const record = error;
131964
+ const data = record.data && typeof record.data === "object" ? record.data : undefined;
131965
+ const message = textField(record, "message") ?? (data ? textField(data, "message") : undefined);
131966
+ const name = textField(record, "name");
131967
+ const code = textField(record, "code");
131968
+ if (message) {
131969
+ if (name)
131970
+ return `${name}: ${message}`;
131971
+ return code ? `${message} (${code})` : message;
131972
+ }
131973
+ if (name)
131974
+ return name;
131975
+ try {
131976
+ return JSON.stringify(error);
131977
+ } catch {
131978
+ return Object.prototype.toString.call(error);
131979
+ }
131980
+ }
131981
+ function toSessionError(error) {
131982
+ if (error instanceof Error)
131983
+ return error;
131984
+ const wrapped = new Error(describeSessionError(error), { cause: error });
131985
+ const name = error && typeof error === "object" ? textField(error, "name") : undefined;
131986
+ if (name)
131987
+ wrapped.name = name;
131988
+ return wrapped;
131989
+ }
131990
+ var SKIPPED_SESSIONS_SHOWN = 5;
131991
+ function formatSkippedSessions(skipped) {
131992
+ if (skipped.length === 0)
131993
+ return "";
131994
+ const noun = skipped.length === 1 ? "session" : "sessions";
131995
+ const shown = skipped.slice(0, SKIPPED_SESSIONS_SHOWN).map((entry) => `${entry.id} (${entry.reason})`);
131996
+ const hidden = skipped.length - shown.length;
131997
+ const entries = hidden > 0 ? [...shown, `and ${hidden} more`] : shown;
131998
+ return `
131999
+
132000
+ Skipped ${skipped.length} ${noun} that could not be read: ${entries.join(", ")}`;
132001
+ }
132002
+
131921
132003
  // packages/omo-opencode/src/tools/session-manager/sdk-storage.ts
131922
132004
  function unwrapSdkResponseError(response) {
131923
132005
  if (!response || typeof response !== "object" || !("error" in response)) {
@@ -131929,7 +132011,7 @@ function throwOnNonFallbackableSdkError(response) {
131929
132011
  const error = unwrapSdkResponseError(response);
131930
132012
  if (!error)
131931
132013
  return;
131932
- throw error;
132014
+ throw toSessionError(error);
131933
132015
  }
131934
132016
  var SDK_TRANSIENT_RETRY_ATTEMPTS = 3;
131935
132017
  var GLOBAL_SESSION_PAGE_SIZE = 100;
@@ -132062,6 +132144,12 @@ function shouldFallbackFromSdkError(error) {
132062
132144
  }
132063
132145
 
132064
132146
  // packages/omo-opencode/src/tools/session-manager/storage.ts
132147
+ function isSdkSessionNotFound(error) {
132148
+ return error instanceof Error && error.name === "NotFoundError";
132149
+ }
132150
+ function shouldReadFileSession(error) {
132151
+ return shouldFallbackFromSdkError(error) || isSdkSessionNotFound(error);
132152
+ }
132065
132153
  function normalizeProjectFilter(directory) {
132066
132154
  if (directory === "/")
132067
132155
  return;
@@ -132096,7 +132184,7 @@ async function getMainSessions(options) {
132096
132184
  } catch (error) {
132097
132185
  if (!shouldFallbackFromSdkError(error))
132098
132186
  throw error;
132099
- log2("[session-manager] falling back to file session list after SDK unavailable error", { error: String(error) });
132187
+ log2("[session-manager] falling back to file session list after SDK unavailable error", { error: describeSessionError(error) });
132100
132188
  }
132101
132189
  }
132102
132190
  return getFileMainSessions(directory);
@@ -132110,7 +132198,7 @@ async function getAllSessions(limit) {
132110
132198
  } catch (error) {
132111
132199
  if (!shouldFallbackFromSdkError(error))
132112
132200
  throw error;
132113
- log2("[session-manager] falling back to file session ids after SDK unavailable error", { error: String(error) });
132201
+ log2("[session-manager] falling back to file session ids after SDK unavailable error", { error: describeSessionError(error) });
132114
132202
  }
132115
132203
  }
132116
132204
  return getFileAllSessions();
@@ -132122,9 +132210,9 @@ async function sessionExists2(sessionID) {
132122
132210
  if (existsInSdk)
132123
132211
  return true;
132124
132212
  } catch (error) {
132125
- if (!shouldFallbackFromSdkError(error))
132213
+ if (!shouldReadFileSession(error))
132126
132214
  throw error;
132127
- log2("[session-manager] falling back to file sessionExists after SDK unavailable error", { error: String(error), sessionID });
132215
+ log2("[session-manager] reading file sessionExists after SDK error", { error: describeSessionError(error), sessionID });
132128
132216
  }
132129
132217
  }
132130
132218
  return fileSessionExists(sessionID);
@@ -132136,9 +132224,9 @@ async function readSessionMessages2(sessionID) {
132136
132224
  if (sdkMessages.length > 0)
132137
132225
  return sdkMessages;
132138
132226
  } catch (error) {
132139
- if (!shouldFallbackFromSdkError(error))
132227
+ if (!shouldReadFileSession(error))
132140
132228
  throw error;
132141
- log2("[session-manager] falling back to file session messages after SDK unavailable error", { error: String(error), sessionID });
132229
+ log2("[session-manager] reading file session messages after SDK error", { error: describeSessionError(error), sessionID });
132142
132230
  }
132143
132231
  }
132144
132232
  return getFileSessionMessages(sessionID);
@@ -132150,9 +132238,9 @@ async function readSessionTodos(sessionID) {
132150
132238
  if (sdkTodos.length > 0)
132151
132239
  return sdkTodos;
132152
132240
  } catch (error) {
132153
- if (!shouldFallbackFromSdkError(error))
132241
+ if (!shouldReadFileSession(error))
132154
132242
  throw error;
132155
- log2("[session-manager] falling back to file session todos after SDK unavailable error", { error: String(error), sessionID });
132243
+ log2("[session-manager] reading file session todos after SDK error", { error: describeSessionError(error), sessionID });
132156
132244
  }
132157
132245
  }
132158
132246
  return getFileSessionTodos(sessionID);
@@ -132194,9 +132282,9 @@ async function getSessionInfo(sessionID) {
132194
132282
  };
132195
132283
  }
132196
132284
  } catch (error) {
132197
- if (!shouldFallbackFromSdkError(error))
132285
+ if (!shouldReadFileSession(error))
132198
132286
  throw error;
132199
- log2("[session-manager] falling back to file session info after SDK unavailable error", { error: String(error), sessionID });
132287
+ log2("[session-manager] reading file session info after SDK error", { error: describeSessionError(error), sessionID });
132200
132288
  }
132201
132289
  }
132202
132290
  return getFileSessionInfo(sessionID);
@@ -132207,9 +132295,14 @@ async function formatSessionList(sessionIDs) {
132207
132295
  if (sessionIDs.length === 0) {
132208
132296
  return "No sessions found.";
132209
132297
  }
132210
- const infos = (await Promise.all(sessionIDs.map((id) => getSessionInfo(id)))).filter((info) => info !== null);
132298
+ const skipped = [];
132299
+ const reads = await Promise.all(sessionIDs.map((id) => getSessionInfo(id).catch((error) => {
132300
+ skipped.push({ id, reason: describeSessionError(error) });
132301
+ return null;
132302
+ })));
132303
+ const infos = reads.filter((info) => info !== null);
132211
132304
  if (infos.length === 0) {
132212
- return "No valid sessions found.";
132305
+ return `No valid sessions found.${formatSkippedSessions(skipped)}`;
132213
132306
  }
132214
132307
  const headers = ["Session ID", "Messages", "First", "Last", "Agents"];
132215
132308
  const rows = infos.map((info) => [
@@ -132225,7 +132318,7 @@ async function formatSessionList(sessionIDs) {
132225
132318
  };
132226
132319
  const separator = "|" + colWidths.map((w) => "-".repeat(w + 2)).join("|") + "|";
132227
132320
  return [formatRow(headers), separator, ...rows.map(formatRow)].join(`
132228
- `);
132321
+ `) + formatSkippedSessions(skipped);
132229
132322
  }
132230
132323
  function formatSessionMessages(messages, includeTodos, todos) {
132231
132324
  if (messages.length === 0) {
@@ -132421,7 +132514,7 @@ function createSessionManagerTools(ctx, deps = {}) {
132421
132514
  }
132422
132515
  return await resolvedDeps.formatSessionList(sessionIDs);
132423
132516
  } catch (e) {
132424
- return `Error: ${e instanceof Error ? e.message : String(e)}`;
132517
+ return `Error: ${describeSessionError(e)}`;
132425
132518
  }
132426
132519
  }
132427
132520
  });
@@ -132449,7 +132542,7 @@ function createSessionManagerTools(ctx, deps = {}) {
132449
132542
  const todos = args.include_todos ? await resolvedDeps.readSessionTodos(args.session_id) : undefined;
132450
132543
  return resolvedDeps.formatSessionMessages(messages, args.include_todos, todos);
132451
132544
  } catch (e) {
132452
- return `Error: ${e instanceof Error ? e.message : String(e)}`;
132545
+ return `Error: ${describeSessionError(e)}`;
132453
132546
  }
132454
132547
  }
132455
132548
  });
@@ -132464,6 +132557,7 @@ function createSessionManagerTools(ctx, deps = {}) {
132464
132557
  execute: async (args, _context) => {
132465
132558
  try {
132466
132559
  const resultLimit = args.limit && args.limit > 0 ? args.limit : 20;
132560
+ const skipped = [];
132467
132561
  const searchOperation = async () => {
132468
132562
  if (args.session_id) {
132469
132563
  return resolvedDeps.searchInSession(args.session_id, args.query, args.case_sensitive, resultLimit);
@@ -132475,15 +132569,19 @@ function createSessionManagerTools(ctx, deps = {}) {
132475
132569
  if (allResults.length >= resultLimit)
132476
132570
  break;
132477
132571
  const remaining = resultLimit - allResults.length;
132478
- const sessionResults = await resolvedDeps.searchInSession(sid, args.query, args.case_sensitive, remaining);
132479
- allResults.push(...sessionResults);
132572
+ try {
132573
+ const sessionResults = await resolvedDeps.searchInSession(sid, args.query, args.case_sensitive, remaining);
132574
+ allResults.push(...sessionResults);
132575
+ } catch (error) {
132576
+ skipped.push({ id: sid, reason: describeSessionError(error) });
132577
+ }
132480
132578
  }
132481
132579
  return allResults.slice(0, resultLimit);
132482
132580
  };
132483
132581
  const results = await withTimeout4(searchOperation(), SEARCH_TIMEOUT_MS, "Search");
132484
- return resolvedDeps.formatSearchResults(results);
132582
+ return resolvedDeps.formatSearchResults(results) + formatSkippedSessions(skipped);
132485
132583
  } catch (e) {
132486
- return `Error: ${e instanceof Error ? e.message : String(e)}`;
132584
+ return `Error: ${describeSessionError(e)}`;
132487
132585
  }
132488
132586
  }
132489
132587
  });
@@ -132500,7 +132598,7 @@ function createSessionManagerTools(ctx, deps = {}) {
132500
132598
  }
132501
132599
  return resolvedDeps.formatSessionInfo(info);
132502
132600
  } catch (e) {
132503
- return `Error: ${e instanceof Error ? e.message : String(e)}`;
132601
+ return `Error: ${describeSessionError(e)}`;
132504
132602
  }
132505
132603
  }
132506
132604
  });
@@ -137865,7 +137963,7 @@ async function createSyncSession(client, input) {
137865
137963
  const createResult = await client.session.create({
137866
137964
  body: {
137867
137965
  parentID: input.parentSessionID,
137868
- title: `${input.description} (@${input.agentToUse} subagent)`,
137966
+ title: `${input.category ? `[${input.category}] ` : ""}${input.description} (@${input.agentToUse} subagent)`,
137869
137967
  permission: QUESTION_DENIED_SESSION_PERMISSION,
137870
137968
  ...input.categoryModel ? {
137871
137969
  model: {
@@ -138297,6 +138395,7 @@ async function runSyncTaskLoop(input) {
138297
138395
  agentToUse,
138298
138396
  description: args.description,
138299
138397
  defaultDirectory: directory,
138398
+ category: args.category,
138300
138399
  categoryModel: nextFallbackModel
138301
138400
  });
138302
138401
  if (!retrySessionResult.ok) {
@@ -138397,6 +138496,7 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
138397
138496
  agentToUse,
138398
138497
  description: args.description,
138399
138498
  defaultDirectory: directory,
138499
+ category: args.category,
138400
138500
  categoryModel
138401
138501
  });
138402
138502
  if (!createSessionResult.ok) {
@@ -138915,7 +139015,7 @@ async function resolveSubagentAgentMatch(requestedAgent, executorCtx, options) {
138915
139015
  const hasDemotedPlan = agents.some(isDemotedPlanAgent);
138916
139016
  const serverPrimaryAgent = findPrimaryAgentMatch(agents, requestedAgent);
138917
139017
  const serverMatchedAgent = findCallableAgentMatch(agents, requestedAgent);
138918
- const mergedAgents = mergeWithClaudeCodeAgents(agents, executorCtx.directory);
139018
+ const mergedAgents = executorCtx.claudeCodeAgentsEnabled === false ? agents : mergeWithClaudeCodeAgents(agents, executorCtx.directory);
138919
139019
  const matchedPrimaryAgent = findPrimaryAgentMatch(mergedAgents, requestedAgent);
138920
139020
  const useHiddenPlanFallback = shouldUseHiddenPlanAgent(requestedAgent, serverPrimaryAgent, serverMatchedAgent, executorCtx.sisyphusAgentConfig, hasDemotedPlan);
138921
139021
  if (isReservedHiddenNativeAgent(requestedAgent) && !serverPrimaryAgent && !serverMatchedAgent) {
@@ -142352,17 +142452,24 @@ class ConcurrencyManager {
142352
142452
  }
142353
142453
  return model;
142354
142454
  }
142355
- async acquire(model, taskId) {
142455
+ tryAcquire(model) {
142356
142456
  const key = this.getConcurrencyKey(model);
142357
142457
  const limit = this.getConcurrencyLimit(model);
142358
142458
  if (limit === Infinity) {
142359
- return;
142459
+ return true;
142360
142460
  }
142361
142461
  const current = this.counts.get(key) ?? 0;
142362
142462
  if (current < limit) {
142363
142463
  this.counts.set(key, current + 1);
142464
+ return true;
142465
+ }
142466
+ return false;
142467
+ }
142468
+ async acquire(model, taskId) {
142469
+ if (this.tryAcquire(model)) {
142364
142470
  return;
142365
142471
  }
142472
+ const key = this.getConcurrencyKey(model);
142366
142473
  return new Promise((resolve, reject) => {
142367
142474
  const queue = this.queues.get(key) ?? [];
142368
142475
  const entry = {
@@ -142665,9 +142772,17 @@ async function tryFallbackRetry(args) {
142665
142772
  const rawKey = task.model ? `${task.model.providerID}/${task.model.modelID}` : task.agent;
142666
142773
  const key = concurrencyManager.getConcurrencyKey(rawKey);
142667
142774
  const queue = queuesByKey.get(key) ?? [];
142775
+ const hasContinuation = !!task.originalPrompt && task.originalPrompt !== task.prompt;
142776
+ const retryPrompt = hasContinuation ? `${task.originalPrompt}
142777
+
142778
+ [Latest continuation instruction]
142779
+ ${task.prompt}` : task.prompt;
142780
+ const retryNote = hasContinuation ? `
142781
+
142782
+ [Note: this is a retry in a new session after the previous session failed; the earlier session's context is not available. Work from the original task and latest continuation instruction above.]` : "";
142668
142783
  const retryInput = {
142669
142784
  description: task.description,
142670
- prompt: task.prompt,
142785
+ prompt: `${retryPrompt}${retryNote}`,
142671
142786
  agent: task.agent,
142672
142787
  parentSessionId: task.parentSessionId,
142673
142788
  parentMessageId: task.parentMessageId,
@@ -144199,7 +144314,7 @@ async function verifySessionExists(client, sessionID, directory) {
144199
144314
 
144200
144315
  // packages/omo-opencode/src/features/background-agent/session-stopped-on-error.ts
144201
144316
  var MIN_ERRORED_IDLE_POLLS = 10;
144202
- function describeSessionError(error) {
144317
+ function describeSessionError2(error) {
144203
144318
  if (!isRecord9(error)) {
144204
144319
  return String(error);
144205
144320
  }
@@ -144217,7 +144332,7 @@ function getStoppedSessionError(messages) {
144217
144332
  if (error === undefined || error === null) {
144218
144333
  return;
144219
144334
  }
144220
- return describeSessionError(error);
144335
+ return describeSessionError2(error);
144221
144336
  }
144222
144337
 
144223
144338
  // packages/omo-opencode/src/features/background-agent/session-idle-event-handler.ts
@@ -144601,6 +144716,25 @@ class TaskHistory {
144601
144716
  clearSession(parentSessionID) {
144602
144717
  this.entries.delete(parentSessionID);
144603
144718
  }
144719
+ restoreEntry(parentSessionID, taskID, entry) {
144720
+ const list = this.entries.get(parentSessionID);
144721
+ if (!list) {
144722
+ if (entry)
144723
+ this.record(parentSessionID, entry);
144724
+ return;
144725
+ }
144726
+ const index = list.findIndex((current) => current.id === taskID);
144727
+ if (index !== -1) {
144728
+ if (entry)
144729
+ list[index] = { ...entry };
144730
+ else
144731
+ list.splice(index, 1);
144732
+ } else if (entry) {
144733
+ this.record(parentSessionID, entry);
144734
+ }
144735
+ if (list.length === 0)
144736
+ this.entries.delete(parentSessionID);
144737
+ }
144604
144738
  clearAll() {
144605
144739
  this.entries.clear();
144606
144740
  }
@@ -144773,6 +144907,7 @@ var TERMINAL_TASK_STATUSES = new Set([
144773
144907
  function pruneStaleTasksAndNotifications(args) {
144774
144908
  const { tasks, notifications, onTaskPruned } = args;
144775
144909
  const effectiveTtl = args.taskTtlMs ?? TASK_TTL_MS;
144910
+ const terminalTtl = Math.max(TERMINAL_TASK_TTL_MS, args.taskCleanupDelayMs ?? 0);
144776
144911
  const now = Date.now();
144777
144912
  const tasksWithPendingNotifications = new Set;
144778
144913
  for (const queued of notifications.values()) {
@@ -144784,11 +144919,15 @@ function pruneStaleTasksAndNotifications(args) {
144784
144919
  if (TERMINAL_TASK_STATUSES.has(task.status)) {
144785
144920
  if (tasksWithPendingNotifications.has(taskId))
144786
144921
  continue;
144922
+ if (args.hasPendingRemovalTimer?.(taskId))
144923
+ continue;
144924
+ if (task.sessionId && args.hasAttachedSyncSession?.(task.sessionId))
144925
+ continue;
144787
144926
  const completedAt = task.completedAt?.getTime();
144788
144927
  if (!completedAt)
144789
144928
  continue;
144790
144929
  const age = now - completedAt;
144791
- if (age <= TERMINAL_TASK_TTL_MS)
144930
+ if (age <= terminalTtl)
144792
144931
  continue;
144793
144932
  removeTaskToastTracking(taskId);
144794
144933
  tasks.delete(taskId);
@@ -145195,6 +145334,7 @@ class BackgroundManager {
145195
145334
  processingKeys = new Set;
145196
145335
  completionTimers = new Map;
145197
145336
  syncAttachedSessions = new Set;
145337
+ resumesAwaitingAdmission = new Set;
145198
145338
  completedTaskArchive = new Map;
145199
145339
  completedTaskSummaries = new Map;
145200
145340
  idleDeferralTimers = new Map;
@@ -145401,8 +145541,12 @@ class BackgroundManager {
145401
145541
  taskIDs.add(task.id);
145402
145542
  this.tasksByParentSession.set(parentSessionID, taskIDs);
145403
145543
  }
145404
- captureResumeTaskSnapshot(task) {
145544
+ captureResumeTaskSnapshot(task, resumeParentSessionID) {
145405
145545
  return {
145546
+ prompt: task.prompt,
145547
+ originalPrompt: task.originalPrompt,
145548
+ queuedAt: task.queuedAt,
145549
+ historyEntry: this.taskHistory.getByParentSession(resumeParentSessionID).find((entry) => entry.id === task.id),
145406
145550
  status: task.status,
145407
145551
  completedAt: task.completedAt,
145408
145552
  error: task.error,
@@ -145418,16 +145562,23 @@ class BackgroundManager {
145418
145562
  };
145419
145563
  }
145420
145564
  restoreTaskAfterSkippedResume(task, snapshot, skippedStatus) {
145565
+ if (this.shutdownTriggered)
145566
+ return;
145421
145567
  log2("[background-agent] Restoring task after skipped resume prompt:", {
145422
145568
  taskId: task.id,
145423
145569
  sessionID: task.sessionId,
145424
145570
  skippedStatus
145425
145571
  });
145572
+ const resumeParentSessionID = task.parentSessionId;
145426
145573
  this.cleanupPendingByParent(task);
145574
+ this.taskHistory.restoreEntry(resumeParentSessionID, task.id, snapshot.historyEntry);
145427
145575
  if (task.concurrencyKey) {
145428
145576
  this.concurrencyManager.release(task.concurrencyKey);
145429
145577
  }
145430
145578
  task.status = snapshot.status;
145579
+ task.prompt = snapshot.prompt;
145580
+ task.originalPrompt = snapshot.originalPrompt;
145581
+ task.queuedAt = snapshot.queuedAt;
145431
145582
  task.completedAt = snapshot.completedAt;
145432
145583
  task.error = snapshot.error;
145433
145584
  task.startedAt = snapshot.startedAt;
@@ -145444,6 +145595,9 @@ class BackgroundManager {
145444
145595
  this.scheduleTaskRemoval(task.id);
145445
145596
  }
145446
145597
  this.updateBackgroundTaskMarker(task.parentSessionId);
145598
+ if (resumeParentSessionID !== task.parentSessionId) {
145599
+ this.updateBackgroundTaskMarker(resumeParentSessionID);
145600
+ }
145447
145601
  }
145448
145602
  removeTaskFromParentIndex(taskID, parentSessionID) {
145449
145603
  if (!parentSessionID) {
@@ -145621,7 +145775,7 @@ class BackgroundManager {
145621
145775
  const createResult = await this.client.session.create({
145622
145776
  body: {
145623
145777
  parentID: input.parentSessionId,
145624
- title: `${input.description} (@${input.agent} subagent)`,
145778
+ title: `${input.category ? `[${input.category}] ` : ""}${input.description} (@${input.agent} subagent)`,
145625
145779
  ...input.sessionPermission ? { permission: input.sessionPermission } : {},
145626
145780
  ...input.model ? {
145627
145781
  model: {
@@ -146040,14 +146194,74 @@ The fallback retry session is now created and can be inspected directly.
146040
146194
  if (existingTask.status === "running") {
146041
146195
  throw new Error(`Task ${existingTask.id} is currently running and cannot accept a continuation prompt. ` + "Wait for it to complete before resuming it with task_id.");
146042
146196
  }
146043
- const resumeSnapshot = this.captureResumeTaskSnapshot(existingTask);
146197
+ if (existingTask.status === "pending" && existingTask.pendingResume) {
146198
+ throw new Error(`Task ${existingTask.id} already has a continuation queued for admission and cannot accept another one. ` + "Wait for it to start and complete before resuming it with task_id.");
146199
+ }
146200
+ if (this.resumesAwaitingAdmission.has(existingTask.id)) {
146201
+ throw new Error(`Task ${existingTask.id} is already being resumed and cannot accept another continuation. ` + "Wait for it to complete before resuming it with task_id.");
146202
+ }
146203
+ const resumeSnapshot = this.captureResumeTaskSnapshot(existingTask, input.parentSessionId);
146044
146204
  const completionTimer = this.completionTimers.get(existingTask.id);
146045
146205
  if (completionTimer) {
146046
146206
  clearTimeout(completionTimer);
146047
146207
  this.completionTimers.delete(existingTask.id);
146048
146208
  }
146049
146209
  const concurrencyKey = this.concurrencyManager.getConcurrencyKey(existingTask.concurrencyGroup ?? existingTask.agent);
146050
- await this.concurrencyManager.acquire(concurrencyKey);
146210
+ if (this.concurrencyManager.tryAcquire(concurrencyKey)) {
146211
+ this.resumesAwaitingAdmission.add(existingTask.id);
146212
+ try {
146213
+ await Promise.resolve();
146214
+ return await this.startResumedTask(existingTask, input, resumeSnapshot, concurrencyKey);
146215
+ } finally {
146216
+ this.resumesAwaitingAdmission.delete(existingTask.id);
146217
+ }
146218
+ }
146219
+ const admission = this.concurrencyManager.acquire(concurrencyKey, existingTask.id);
146220
+ const resumeToken = {};
146221
+ existingTask.pendingResume = resumeToken;
146222
+ existingTask.status = "pending";
146223
+ existingTask.queuedAt = new Date;
146224
+ existingTask.completedAt = undefined;
146225
+ existingTask.error = undefined;
146226
+ this.updateTaskParent(existingTask, input.parentSessionId);
146227
+ existingTask.parentMessageId = input.parentMessageId;
146228
+ existingTask.parentModel = input.parentModel;
146229
+ existingTask.parentAgent = input.parentAgent;
146230
+ if (input.parentTools)
146231
+ existingTask.parentTools = input.parentTools;
146232
+ if (input.parentSessionId) {
146233
+ const pending = this.pendingByParent.get(input.parentSessionId) ?? new Set;
146234
+ pending.add(existingTask.id);
146235
+ this.pendingByParent.set(input.parentSessionId, pending);
146236
+ }
146237
+ this.taskHistory.record(input.parentSessionId, { id: existingTask.id, sessionID: existingTask.sessionId, agent: existingTask.agent, description: existingTask.description, status: "pending", category: existingTask.category });
146238
+ getTaskToastManager()?.addTask({ id: existingTask.id, description: existingTask.description, agent: existingTask.agent, isBackground: true, status: "queued" });
146239
+ this.updateBackgroundTaskMarker(existingTask.parentSessionId);
146240
+ admission.then(async () => {
146241
+ if (this.shutdownTriggered || existingTask.status !== "pending" || existingTask.pendingResume !== resumeToken) {
146242
+ this.concurrencyManager.release(concurrencyKey);
146243
+ return;
146244
+ }
146245
+ await this.startResumedTask(existingTask, input, resumeSnapshot, concurrencyKey);
146246
+ }).catch((error) => {
146247
+ this.logger("[background-agent] Queued resume was not admitted:", { taskId: existingTask.id, error: error instanceof Error ? error.message : String(error) });
146248
+ if (this.shutdownTriggered || existingTask.status !== "pending" || existingTask.pendingResume !== resumeToken)
146249
+ return;
146250
+ existingTask.pendingResume = undefined;
146251
+ this.restoreTaskAfterSkippedResume(existingTask, resumeSnapshot, "unavailable");
146252
+ });
146253
+ return existingTask;
146254
+ }
146255
+ async startResumedTask(existingTask, input, resumeSnapshot, concurrencyKey) {
146256
+ if (this.shutdownTriggered) {
146257
+ this.concurrencyManager.release(concurrencyKey);
146258
+ return existingTask;
146259
+ }
146260
+ if (!existingTask.sessionId)
146261
+ throw new Error(`Task has no sessionID: ${existingTask.id}`);
146262
+ existingTask.pendingResume = undefined;
146263
+ existingTask.originalPrompt ??= existingTask.prompt;
146264
+ existingTask.prompt = input.prompt;
146051
146265
  existingTask.concurrencyKey = concurrencyKey;
146052
146266
  existingTask.concurrencyGroup = concurrencyKey;
146053
146267
  existingTask.status = "running";
@@ -146061,6 +146275,15 @@ The fallback retry session is now created and can be inspected directly.
146061
146275
  existingTask.parentTools = input.parentTools;
146062
146276
  }
146063
146277
  existingTask.startedAt = new Date;
146278
+ this.taskHistory.record(input.parentSessionId, {
146279
+ id: existingTask.id,
146280
+ sessionID: existingTask.sessionId,
146281
+ agent: existingTask.agent,
146282
+ description: existingTask.description,
146283
+ status: "running",
146284
+ category: existingTask.category,
146285
+ startedAt: existingTask.startedAt
146286
+ });
146064
146287
  existingTask.progress = {
146065
146288
  toolCalls: existingTask.progress?.toolCalls ?? 0,
146066
146289
  toolCallWindow: existingTask.progress?.toolCallWindow,
@@ -146171,6 +146394,16 @@ The fallback retry session is now created and can be inspected directly.
146171
146394
  const errorMessage = errorInfo.message ?? (error instanceof Error ? error.message : String(error));
146172
146395
  existingTask.error = errorMessage;
146173
146396
  existingTask.completedAt = new Date;
146397
+ this.taskHistory.record(existingTask.parentSessionId, {
146398
+ id: existingTask.id,
146399
+ sessionID: existingTask.sessionId,
146400
+ agent: existingTask.agent,
146401
+ description: existingTask.description,
146402
+ status: "interrupt",
146403
+ category: existingTask.category,
146404
+ startedAt: existingTask.startedAt,
146405
+ completedAt: existingTask.completedAt
146406
+ });
146174
146407
  if (existingTask.rootSessionId) {
146175
146408
  this.unregisterRootDescendant(existingTask.rootSessionId);
146176
146409
  }
@@ -147257,7 +147490,10 @@ The task was re-queued on a fallback model after a retryable failure.
147257
147490
  tasks: this.tasks,
147258
147491
  notifications: this.notifications,
147259
147492
  taskTtlMs: this.config?.taskTtlMs,
147493
+ taskCleanupDelayMs: this.config?.taskCleanupDelayMs,
147260
147494
  sessionStatuses: allStatuses,
147495
+ hasPendingRemovalTimer: (taskId) => this.completionTimers.has(taskId),
147496
+ hasAttachedSyncSession: (sessionID) => this.syncAttachedSessions.has(sessionID),
147261
147497
  onTaskPruned: (taskId, task, errorMessage) => {
147262
147498
  const wasPending = task.status === "pending";
147263
147499
  log2("[background-agent] Pruning stale task:", { taskId, status: task.status, age: Math.round(((wasPending ? task.queuedAt?.getTime() : task.startedAt?.getTime()) ? Date.now() - (wasPending ? task.queuedAt.getTime() : task.startedAt.getTime()) : 0) / 1000) + "s" });
@@ -147488,7 +147724,17 @@ The task was re-queued on a fallback model after a retryable failure.
147488
147724
  this.stopPolling();
147489
147725
  const trackedSessionIDs = new Set;
147490
147726
  const abortRequests = [];
147727
+ const queuedResumeParents = new Set;
147491
147728
  for (const task of this.tasks.values()) {
147729
+ if (task.pendingResume) {
147730
+ queuedResumeParents.add(task.parentSessionId);
147731
+ task.pendingResume = undefined;
147732
+ task.status = "cancelled";
147733
+ task.completedAt = new Date;
147734
+ this.concurrencyManager.cancelWaiter(task.concurrencyGroup ?? task.agent, task.id);
147735
+ this.cleanupPendingByParent(task);
147736
+ removeTaskToastTracking(task.id);
147737
+ }
147492
147738
  if (task.sessionId) {
147493
147739
  trackedSessionIDs.add(task.sessionId);
147494
147740
  }
@@ -147554,6 +147800,9 @@ The task was re-queued on a fallback model after a retryable failure.
147554
147800
  this.queuesByKey.clear();
147555
147801
  this.processingKeys.clear();
147556
147802
  this.taskHistory.clearAll();
147803
+ for (const parentSessionID of queuedResumeParents) {
147804
+ this.updateBackgroundTaskMarker(parentSessionID);
147805
+ }
147557
147806
  this.completedTaskSummaries.clear();
147558
147807
  this.unregisterProcessCleanup();
147559
147808
  log2("[background-agent] Shutdown complete");
@@ -172359,7 +172608,8 @@ function createTeamModeToolsRecord(args) {
172359
172608
  team_create: factories.createTeamCreateTool(pluginConfig.team_mode, ctx.client, managers.backgroundManager, managers.tmuxSessionManager, {
172360
172609
  userCategories: pluginConfig.categories,
172361
172610
  sisyphusJuniorModel: getSisyphusJuniorModelOverride(pluginConfig.agents?.["sisyphus-junior"]),
172362
- agentOverrides: pluginConfig.agents
172611
+ agentOverrides: pluginConfig.agents,
172612
+ claudeCodeAgentsEnabled: pluginConfig.claude_code?.agents ?? true
172363
172613
  }),
172364
172614
  team_delete: factories.createTeamDeleteTool(pluginConfig.team_mode, ctx.client, managers.backgroundManager, managers.tmuxSessionManager),
172365
172615
  team_shutdown_request: factories.createTeamShutdownRequestTool(pluginConfig.team_mode, ctx.client),
@@ -172505,6 +172755,7 @@ function createCoreTools(args) {
172505
172755
  nativeSkills,
172506
172756
  getLoadedSkills,
172507
172757
  sisyphusAgentConfig: pluginConfig.sisyphus_agent,
172758
+ claudeCodeAgentsEnabled: pluginConfig.claude_code?.agents ?? true,
172508
172759
  syncPollTimeoutMs: pluginConfig.background_task?.syncPollTimeoutMs,
172509
172760
  modelFallbackControllerAccessor: managers.modelFallbackControllerAccessor,
172510
172761
  onSyncSessionCreated: async (event) => {
@@ -173657,7 +173908,8 @@ function createTeamCreateTool(config, client, bgMgr, tmuxMgr, executorConfig, de
173657
173908
  directory: projectRoot,
173658
173909
  userCategories: executorConfig?.userCategories,
173659
173910
  sisyphusJuniorModel: executorConfig?.sisyphusJuniorModel,
173660
- agentOverrides: executorConfig?.agentOverrides
173911
+ agentOverrides: executorConfig?.agentOverrides,
173912
+ claudeCodeAgentsEnabled: executorConfig?.claudeCodeAgentsEnabled
173661
173913
  }, config, bgMgr, tmuxMgr, {
173662
173914
  callerAgentTypeId: callerTeamLead.agentTypeId,
173663
173915
  parentMessageID: runtimeContext.messageID
@@ -174565,7 +174817,7 @@ function buildChatParamsInput(raw) {
174565
174817
  return null;
174566
174818
  const providerID = model.providerID;
174567
174819
  const modelID = typeof model.modelID === "string" ? model.modelID : typeof model.id === "string" ? model.id : undefined;
174568
- const providerId = provider.id;
174820
+ const providerId = isRecord9(provider.info) ? provider.info.id : provider.id;
174569
174821
  if (typeof providerID !== "string")
174570
174822
  return null;
174571
174823
  if (typeof modelID !== "string")
@@ -174578,7 +174830,8 @@ function buildChatParamsInput(raw) {
174578
174830
  model: { providerID, modelID },
174579
174831
  provider: { id: providerId },
174580
174832
  message,
174581
- rawMessage: message
174833
+ rawMessage: message,
174834
+ runtimeModel: isRecord9(model.capabilities) ? model : undefined
174582
174835
  };
174583
174836
  }
174584
174837
  function isChatParamsOutput(raw) {
@@ -174616,7 +174869,8 @@ function createChatParamsHandler(_args = {}) {
174616
174869
  }
174617
174870
  const capabilities = getModelCapabilities2({
174618
174871
  providerID: normalizedInput.model.providerID,
174619
- modelID: normalizedInput.model.modelID
174872
+ modelID: normalizedInput.model.modelID,
174873
+ runtimeModel: normalizedInput.runtimeModel
174620
174874
  });
174621
174875
  const compatibility = resolveCompatibleModelSettings({
174622
174876
  providerID: normalizedInput.model.providerID,
@@ -174950,7 +175204,6 @@ async function resolveValidUltraworkVariant(client, model, variant) {
174950
175204
  // packages/omo-opencode/src/plugin/ultrawork-model-override.ts
174951
175205
  var CODE_BLOCK = /```[\s\S]*?```/g;
174952
175206
  var INLINE_CODE = /`[^`]+`/g;
174953
- var ULTRAWORK_PATTERN = /\b(ultrawork|ulw)\b/i;
174954
175207
  function detectUltrawork(text) {
174955
175208
  const clean = text.replace(CODE_BLOCK, "").replace(INLINE_CODE, "");
174956
175209
  return ULTRAWORK_PATTERN.test(clean);