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.
- package/dist/cli/index.js +25 -19
- package/dist/cli/service-auth/commands.d.ts +11 -0
- package/dist/cli-node/index.js +25 -19
- package/dist/features/background-agent/concurrency.d.ts +6 -0
- package/dist/features/background-agent/manager.d.ts +2 -0
- package/dist/features/background-agent/task-history.d.ts +1 -0
- package/dist/features/background-agent/task-poller.d.ts +3 -0
- package/dist/features/background-agent/types.d.ts +2 -0
- package/dist/features/team-mode/tools/lifecycle-inline-spec.d.ts +1 -0
- package/dist/hooks/keyword-detector/constants.d.ts +1 -0
- package/dist/index.js +319 -66
- package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
- package/dist/skills/ulw-execute/SKILL.md +10 -7
- package/dist/skills/ulw-research/SKILL.md +4 -4
- package/dist/tools/delegate-task/executor-types.d.ts +1 -0
- package/dist/tools/delegate-task/sync-session-creator.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +2 -0
- package/dist/tools/session-manager/session-error.d.ts +7 -0
- package/dist/tui.js +18 -16
- package/package.json +14 -14
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -2
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +7 -4
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +3 -3
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +18 -15
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +18 -15
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +2 -2
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +7 -4
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +3 -3
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +18 -15
- package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +10 -7
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +4 -4
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -2
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +6 -4
- package/packages/prompts-core/prompts/ultrawork/codex.md +18 -15
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/ulw-execute/SKILL.md +10 -7
- 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.
|
|
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.
|
|
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.
|
|
99372
|
-
"oh-my-opencode-darwin-x64": "5.1.
|
|
99373
|
-
"oh-my-opencode-darwin-x64-baseline": "5.1.
|
|
99374
|
-
"oh-my-opencode-linux-arm64": "5.1.
|
|
99375
|
-
"oh-my-opencode-linux-arm64-musl": "5.1.
|
|
99376
|
-
"oh-my-opencode-linux-x64": "5.1.
|
|
99377
|
-
"oh-my-opencode-linux-x64-baseline": "5.1.
|
|
99378
|
-
"oh-my-opencode-linux-x64-musl": "5.1.
|
|
99379
|
-
"oh-my-opencode-linux-x64-musl-baseline": "5.1.
|
|
99380
|
-
"oh-my-opencode-windows-arm64": "5.1.
|
|
99381
|
-
"oh-my-opencode-windows-x64": "5.1.
|
|
99382
|
-
"oh-my-opencode-windows-x64-baseline": "5.1.
|
|
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
|
|
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 +
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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 (
|
|
121798
|
-
log2(`[${HOOK_NAME13}] Overriding active retry due to provider
|
|
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 (
|
|
121872
|
-
log2(`[${HOOK_NAME13}] Clearing pending fallback due to provider
|
|
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:
|
|
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:
|
|
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 (!
|
|
132213
|
+
if (!shouldReadFileSession(error))
|
|
132126
132214
|
throw error;
|
|
132127
|
-
log2("[session-manager]
|
|
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 (!
|
|
132227
|
+
if (!shouldReadFileSession(error))
|
|
132140
132228
|
throw error;
|
|
132141
|
-
log2("[session-manager]
|
|
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 (!
|
|
132241
|
+
if (!shouldReadFileSession(error))
|
|
132154
132242
|
throw error;
|
|
132155
|
-
log2("[session-manager]
|
|
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 (!
|
|
132285
|
+
if (!shouldReadFileSession(error))
|
|
132198
132286
|
throw error;
|
|
132199
|
-
log2("[session-manager]
|
|
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
|
|
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
|
|
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: ${
|
|
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: ${
|
|
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
|
-
|
|
132479
|
-
|
|
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: ${
|
|
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: ${
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
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 <=
|
|
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
|
-
|
|
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
|
-
|
|
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);
|