oh-my-opencode 5.0.0-beta.40 → 5.0.0-beta.42

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 (126) hide show
  1. package/.agents/skills/pre-publish-review/SKILL.md +9 -12
  2. package/.opencode/skills/pre-publish-review/SKILL.md +9 -12
  3. package/dist/cli/index.js +21 -15
  4. package/dist/cli-node/index.js +21 -15
  5. package/dist/index.js +23 -20
  6. package/dist/skills/ast-grep/SKILL.md +1 -1
  7. package/dist/skills/coding-agent-sessions/SKILL.md +1 -1
  8. package/dist/skills/data-scientist/SKILL.md +1 -1
  9. package/dist/skills/debugging/SKILL.md +1 -1
  10. package/dist/skills/frontend/SKILL.md +1 -1
  11. package/dist/skills/git-master/SKILL.md +1 -1
  12. package/dist/skills/init-deep/SKILL.md +1 -1
  13. package/dist/skills/lsp-setup/SKILL.md +1 -1
  14. package/dist/skills/programming/SKILL.md +1 -1
  15. package/dist/skills/refactor/SKILL.md +1 -1
  16. package/dist/skills/remove-ai-slops/SKILL.md +1 -1
  17. package/dist/skills/review-work/SKILL.md +99 -417
  18. package/dist/skills/ultimate-browsing/ATTRIBUTION.md +1 -1
  19. package/dist/skills/ultimate-browsing/SKILL.md +24 -9
  20. package/dist/skills/ultimate-browsing/references/chrome-stealth.md +13 -10
  21. package/dist/skills/ulw-execute/SKILL.md +1 -1
  22. package/dist/skills/ulw-plan/SKILL.md +1 -1
  23. package/dist/skills/ulw-research/SKILL.md +5 -2
  24. package/dist/skills/visual-qa/SKILL.md +2 -2
  25. package/package.json +17 -14
  26. package/packages/lsp-core/src/lsp/json-rpc-connection.ts +11 -6
  27. package/packages/lsp-daemon/dist/cli.js +11 -6
  28. package/packages/lsp-daemon/dist/client.js +11 -6
  29. package/packages/lsp-daemon/dist/index.js +11 -6
  30. package/packages/lsp-tools-mcp/dist/cli.js +11 -6
  31. package/packages/lsp-tools-mcp/dist/lsp/manager.js +11 -6
  32. package/packages/lsp-tools-mcp/dist/mcp.js +11 -6
  33. package/packages/lsp-tools-mcp/dist/tools.js +11 -6
  34. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  35. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  38. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  40. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  41. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  42. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  44. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +11 -6
  45. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  46. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  48. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  52. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  53. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  54. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
  56. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  58. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  59. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
  60. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  78. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  80. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  81. package/packages/omo-codex/plugin/package-lock.json +12 -12
  82. package/packages/omo-codex/plugin/package.json +1 -1
  83. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +6 -18
  84. package/packages/omo-codex/plugin/skills/ast-grep/SKILL.md +1 -1
  85. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +1 -1
  86. package/packages/omo-codex/plugin/skills/data-scientist/SKILL.md +1 -1
  87. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +1 -1
  88. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
  89. package/packages/omo-codex/plugin/skills/git-master/SKILL.md +1 -1
  90. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
  91. package/packages/omo-codex/plugin/skills/lsp-setup/SKILL.md +1 -1
  92. package/packages/omo-codex/plugin/skills/programming/SKILL.md +1 -1
  93. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +1 -1
  94. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
  95. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +99 -417
  96. package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +1 -1
  97. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +24 -9
  98. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +13 -10
  99. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +1 -1
  100. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
  101. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +5 -2
  102. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +2 -2
  103. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +1 -1
  104. package/packages/omo-codex/scripts/install-dist/install-local.mjs +3 -2
  105. package/packages/shared-skills/package.json +7 -1
  106. package/packages/shared-skills/skill-source-filter.d.ts +16 -0
  107. package/packages/shared-skills/skill-source-filter.mjs +44 -0
  108. package/packages/shared-skills/skills/ast-grep/SKILL.md +1 -1
  109. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +1 -1
  110. package/packages/shared-skills/skills/data-scientist/SKILL.md +1 -1
  111. package/packages/shared-skills/skills/debugging/SKILL.md +1 -1
  112. package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
  113. package/packages/shared-skills/skills/git-master/SKILL.md +1 -1
  114. package/packages/shared-skills/skills/init-deep/SKILL.md +1 -1
  115. package/packages/shared-skills/skills/lsp-setup/SKILL.md +1 -1
  116. package/packages/shared-skills/skills/programming/SKILL.md +1 -1
  117. package/packages/shared-skills/skills/refactor/SKILL.md +1 -1
  118. package/packages/shared-skills/skills/remove-ai-slops/SKILL.md +1 -1
  119. package/packages/shared-skills/skills/review-work/SKILL.md +99 -417
  120. package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +1 -1
  121. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +24 -9
  122. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +13 -10
  123. package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
  124. package/packages/shared-skills/skills/ulw-plan/SKILL.md +1 -1
  125. package/packages/shared-skills/skills/ulw-research/SKILL.md +5 -2
  126. package/packages/shared-skills/skills/visual-qa/SKILL.md +2 -2
@@ -103,7 +103,7 @@ SOFTWARE.
103
103
  ## 4. agent-browser (vercel-labs) — Tier-2 CDP automation CLI (runtime dependency)
104
104
 
105
105
  The Tier-2 automation CLI is **agent-browser**, installed at runtime via `npm`
106
- (`npm i -g agent-browser`). No agent-browser source is vendored in this repository.
106
+ (`bun add -g agent-browser`). No agent-browser source is vendored in this repository.
107
107
 
108
108
  - Source: https://github.com/vercel-labs/agent-browser
109
109
  - Pinned runtime version: **0.34.0** (documented in `references/chrome-stealth.md`;
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: ultimate-browsing
3
- description: "Escalation skill for blocked or hard-to-reach web access — load it when a normal browse/fetch is blocked (WAF, 403, Cloudflare, JS-only render, login-gated, or a platform a generic fetcher cannot read). Tiered router: TIER 1 insane-search (headless extraction + WAF bypass via curl_cffi TLS impersonation, yt-dlp, Jina Reader, public APIs, Playwright real-Chrome fallback); TIER 1.5 agent-reach (platform-native readers for Chinese and social platforms: Xiaohongshu, Douyin, Weibo, Bilibili, V2EX, WeChat, plus Twitter/Reddit/LinkedIn/GitHub); TIER 2 Chrome stealth (CloakBrowser stealth Chromium + agent-browser CDP for clicks, forms, screenshots, video, cookie login). Triggers: blocked site, bypass bot detection, cloudflare/WAF bypass, scrape, stealth browser, import cookies, fill form, screenshot, play youtube, xiaohongshu, douyin, weibo, bilibili, v2ex, wechat article, podcast transcript. NOT for simple searches (use web-search) or plain fetches (use webfetch)."
3
+ description: "Reaches web pages a plain fetch cannot: renders JS, drives clicks and forms, captures screenshots, holds a login, and gets past WAF blocks through platform-native readers and stealth Chrome. Use for any page work beyond retrieving static text."
4
4
  ---
5
5
 
6
6
  # Ultimate Browsing
7
7
 
8
- Escalation web access for tasks a normal browse or fetch cannot complete. Reach for this skill the moment a page is blocked (WAF / 403 / Cloudflare), needs JS rendering, hides behind a login, or lives on a platform a generic fetcher cannot read. Escalate only when the cheaper tier cannot do the job:
8
+ Web access for everything a plain fetch cannot finish: a page that renders in JS, a click or a form, a screenshot, a login that must persist across pages, or a host that blocks generic fetchers (WAF / 403 / Cloudflare). Start at the cheapest tier that can do the job and climb only when it cannot:
9
9
 
10
- **Tier 1 — insane-search** (headless extraction + WAF bypass) -> **Tier 1.5 — agent-reach** (platform-native APIs, esp. Chinese platforms) -> **Tier 2 — Chrome stealth** (real interaction via CloakBrowser + agent-browser).
10
+ **Tier 1 — insane-search** (headless extraction + WAF bypass) -> **Tier 1.5 — agent-reach** (platform-native APIs, esp. Chinese platforms) -> **Tier 2 — a real browser**: 2a a code-driven kernel browser, 2b Chrome stealth (CloakBrowser + agent-browser) when the page fights back.
11
11
 
12
12
  ## PHASE 0 — ROUTE FIRST (MANDATORY)
13
13
 
@@ -23,11 +23,11 @@ User request
23
23
  +- podcast transcript / stock forum ----------------- TIER 1.5 agent-reach
24
24
  +- Twitter feed / LinkedIn profile / GitHub via CLI - TIER 1.5 agent-reach
25
25
  |
26
- +- Tier 1/1.5 returned empty or partial ------------- TIER 2 Chrome stealth
27
- +- click / fill form / scroll / interact ------------ TIER 2 Chrome stealth
28
- +- screenshot / render / play video ----------------- TIER 2 Chrome stealth
29
- +- login session across pages / inject cookies ------ TIER 2 Chrome stealth
30
- +- test web app / QA / dogfood ---------------------- TIER 2 Chrome stealth
26
+ +- Tier 1/1.5 returned empty or partial ------------- TIER 2 2a kernel browser -> 2b stealth
27
+ +- click / fill form / scroll / interact ------------ TIER 2 2a kernel browser -> 2b stealth
28
+ +- screenshot / render / play video ----------------- TIER 2 2a kernel browser -> 2b stealth
29
+ +- login session across pages / inject cookies ------ TIER 2 2b Chrome stealth (profile + cookies)
30
+ +- test web app / QA / dogfood ---------------------- TIER 2 2a kernel browser -> 2b stealth
31
31
  |
32
32
  +- simple search query ------------------------------ NOT this skill (use web-search)
33
33
  ```
@@ -80,10 +80,25 @@ curl -s "https://www.v2ex.com/api/topics/hot.json" # V2EX public API
80
80
 
81
81
  Routing table, per-platform auth (set `TWITTER_*` env vars, `gh auth login`, a transcription key — only if you have access), rate-limit notes, and known version quirks are in [references/agent-reach/README.md](references/agent-reach/README.md).
82
82
 
83
- ## Tier 2 — Chrome stealth (real interaction)
83
+ ## Tier 2 — a real browser (real interaction)
84
84
 
85
85
  **When**: real interaction is needed (clicks, forms, screenshots, video, persistent login), or Tier 1/1.5 failed.
86
86
 
87
+ ### Tier 2a — kernel browser (default)
88
+
89
+ Drive the page from the code cell you are already in, with no CLI process and no open CDP port. On a Bun >= 1.4 runtime that is `new Bun.WebView()` (`navigate`, `click`, `type`, `evaluate`, `screenshot`, raw `cdp`); elsewhere it is `playwright-core`/`puppeteer-core` against the local Chrome. Snapshots and screenshots come back in-process, so this is the cheapest way to answer "what does the page actually render".
90
+
91
+ ```js
92
+ await using view = new Bun.WebView({ width: 1280, height: 800 })
93
+ await view.navigate(url)
94
+ const title = await view.evaluate("document.title")
95
+ await view.screenshot({ path: "/tmp/page.png" })
96
+ ```
97
+
98
+ Climb to 2b when the site detects automation (Turnstile, FingerprintJS, "unusual traffic"), when the flow needs a persistent logged-in profile or injected cookies, or when the kernel browser cannot reach the page at all.
99
+
100
+ ### Tier 2b — Chrome stealth (blocked or logged-in pages)
101
+
87
102
  CloakBrowser is a stealth Chromium with source-level fingerprint patches that passes Cloudflare Turnstile, FingerprintJS, BrowserScan, and 30+ detectors; agent-browser is the CDP automation CLI that drives it. Both are runtime-installed tools (not vendored here). Full setup, version pins, launch flow, cookie login, and cross-platform notes are in [references/chrome-stealth.md](references/chrome-stealth.md).
88
103
 
89
104
  NEVER clear cookies, cache, or site data (`Network.clearBrowserCookies`, `Storage.clearCookies`, `chrome.browsingData.remove`, "clear browsing data") on the user's real/main browser profile — it wipes their logged-in state everywhere. If the task needs that profile's login state, clone the profile directory first (`rsync -a <profile>/ <tmp-clone>/`) and launch CloakBrowser / agent-browser with the clone as the user-data-dir; run any clearing on the clone only.
@@ -17,22 +17,24 @@ CloakBrowser (stealth Chromium) <- CDP port 9242 -> agent-browser CLI
17
17
 
18
18
  CloakBrowser runs in a dedicated Python venv. Cross-platform: macOS, Linux, and Windows all supported by both tools (use the venv path convention for your OS).
19
19
 
20
+ The venv lives at a fixed path so every session reuses one stealth-Chromium download instead of creating a new venv per working directory, and every command below calls its interpreter by absolute path — no `activate` step to forget.
21
+
20
22
  ```bash
21
23
  # CloakBrowser (MIT wrapper source; separate binary license, pin 0.5.7):
22
- uv venv .cloak-venv --python 3.13
23
- # macOS/Linux: source .cloak-venv/bin/activate Windows: .cloak-venv\Scripts\activate
24
- uv pip install "cloakbrowser==0.5.7"
25
- python -c "import cloakbrowser; cloakbrowser.ensure_binary()" # downloads stealth Chromium on first import
24
+ VENV="$HOME/.agents/cloak-venv" # Windows: %USERPROFILE%\.agents\cloak-venv
25
+ uv venv "$VENV" --python 3.13
26
+ uv pip install --python "$VENV/bin/python" "cloakbrowser==0.5.7"
27
+ "$VENV/bin/python" -c "import cloakbrowser; cloakbrowser.ensure_binary()" # downloads stealth Chromium on first import
26
28
 
27
29
  # agent-browser (Apache-2.0, pin 0.34.0):
28
- npm i -g agent-browser@0.34.0 && agent-browser install
30
+ bun add -g agent-browser@0.34.0 && agent-browser install
29
31
  agent-browser --version # 0.34.0
30
32
  ```
31
33
 
32
34
  Verify CloakBrowser:
33
35
 
34
36
  ```bash
35
- python -c "import cloakbrowser; print(cloakbrowser.__version__, cloakbrowser.CHROMIUM_VERSION, cloakbrowser.binary_info()['installed'])"
37
+ "$VENV/bin/python" -c "import cloakbrowser; print(cloakbrowser.__version__, cloakbrowser.CHROMIUM_VERSION, cloakbrowser.binary_info()['installed'])"
36
38
  # -> 0.5.7 <chromium-version> True
37
39
  ```
38
40
 
@@ -41,8 +43,9 @@ python -c "import cloakbrowser; print(cloakbrowser.__version__, cloakbrowser.CHR
41
43
  NEVER clear cookies, cache, or site data (`Network.clearBrowserCookies`, `Storage.clearCookies`, `chrome.browsingData.remove`, "clear browsing data") on the user's real/main browser profile — it wipes their logged-in state everywhere. If you need that profile's login state, clone it first (`rsync -a <profile>/ <tmp-clone>/`) and launch with the clone as the user-data-dir; run any clearing on the clone only.
42
44
 
43
45
  ```bash
44
- # 1. Launch CloakBrowser with CDP on :9242 (background). With the venv active:
45
- python -c "import asyncio,cloakbrowser; asyncio.run(cloakbrowser.launch_async(headless=False, stealth_args=True, args=['--remote-debugging-port=9242']))" &
46
+ # 1. Launch CloakBrowser with CDP on :9242 (background). cloakbrowser lives only in the venv,
47
+ # so call its interpreter by absolute path — this works in any shell, activated or not:
48
+ "$HOME/.agents/cloak-venv/bin/python" -c "import asyncio,cloakbrowser; asyncio.run(cloakbrowser.launch_async(headless=False, stealth_args=True, args=['--remote-debugging-port=9242']))" &
46
49
 
47
50
  # 2. CloakBrowser launches tabless -> agent-browser would say "No page found".
48
51
  # Open the first tab via CDP before any agent-browser command:
@@ -119,6 +122,6 @@ lsof -ti:9242 | xargs kill -9
119
122
  # agent-browser can't connect:
120
123
  curl -s http://127.0.0.1:9242/json/version | head -5 # empty -> CloakBrowser not running
121
124
  # Update either tool:
122
- uv pip install --upgrade "cloakbrowser==0.5.7" && python -c "import cloakbrowser; cloakbrowser.ensure_binary()"
123
- npm i -g agent-browser@0.34.0
125
+ uv pip install --python "$HOME/.agents/cloak-venv/bin/python" --upgrade "cloakbrowser==0.5.7" && "$HOME/.agents/cloak-venv/bin/python" -c "import cloakbrowser; cloakbrowser.ensure_binary()"
126
+ bun add -g agent-browser@0.34.0
124
127
  ```
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ulw-execute
3
- description: "Execute a Prometheus work plan with Boulder state, evidence ledger updates, worktree discipline, parallel subagents, and Stop-hook continuation. Use after planning when the user says ulw-execute, execute plan, continue plan, resume plan, or asks to run a .omo/plans plan."
3
+ description: "Executes a written Prometheus work plan with Boulder state, evidence ledger, worktree discipline, and parallel subagents. Use when the user says ulw-execute or asks to run a .omo/plans plan."
4
4
  ---
5
5
 
6
6
  ## ABSOLUTE RULE: YOU ARE AN ORCHESTRATOR — NEVER THE IMPLEMENTER
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ulw-plan
3
- description: "ACTIVATES ONLY on an explicit user request for the ulw-plan workflow: the user themselves saying ulw-plan, ulw plan, /skill:ulw-plan, or asking in their own words for a work plan before coding. NEVER self-activates: a bare ulw/ultrawork run, an agent-side routing decision, or reading this file is not a request, and the plan-gated reviewers (metis/momus) stay locked without a user request plus a written .omo/plans plan file. Explore-first planning consultant (Prometheus) that grounds in the codebase, asks only the forks exploration cannot resolve - or researches them to best practice when the intent is fuzzy - waits for explicit approval, then writes ONE decision-complete work plan a worker executes with zero further interview. Triggers: ulw-plan, ulw plan, plan this, make a plan, plan before coding, interview me, break this down, start planning, plan mode."
3
+ description: "Explore-first planning consultant that writes one decision-complete work plan before coding. Use only on an explicit user request for the ulw-plan workflow or a plan before implementation; it never self-activates on a bare ulw run."
4
4
  metadata:
5
5
  short-description: Explore-first planning consultant that waits for your okay before planning
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ulw-research
3
- description: "Maximum-saturation research orchestration: ALWAYS proposes the final materials first (PDF+DOCX default), then parallel explore+librarian swarms across codebase, web, official docs, and OSS repos — max-roster teammode when the harness has it — with live journaling, a recursive EXPAND loop driven by leads workers return in message text, empirical verification by running code, and a cited synthesis with charts/Mermaid/assets behind a mandatory visual-QA gate. ACTIVATES ONLY on an explicit user demand for research — the word 'ulw-research' in any prefix form, any 'ulw' research wording including combined invocations like 'mass ulw research', 'ultradebate' or 'hyperdebate' research requests, or an explicit request for research / deep research / an ultra-precise investigation, in any language. Never self-activates for ordinary questions, debugging, or implementation context-gathering. While active it overrides exploration-bounding defaults: exhaustive coverage is the goal."
3
+ description: "Runs maximum-saturation research with a cooperating team, claim-graph gating, and a cited, QA'd deliverable. Use when the user explicitly asks for research or a deep investigation, including any 'ulw' research wording."
4
4
  ---
5
5
 
6
6
  ## Codex Harness Tool Compatibility
@@ -175,13 +175,15 @@ Scaling floor — more angles always justify more workers:
175
175
  | Multi-faceted | 4 | 6 | 2 | 2 | 14 |
176
176
  | Full due diligence | 4 | 6 | 3 | 2 | 15 |
177
177
 
178
+ The browsing column is BINDING, not advisory: when the brief says `Browsing: yes`, the roster names a browsing-worker angle before the first wave launches, and that worker is spawned in the same turn as the rest of the wave. A run that reaches wave 2 with zero browsing workers on a `Browsing: yes` brief has silently downgraded every source to what plain fetch happened to return.
179
+
178
180
  **Disambiguate before you expand.** When the topic names something that could resolve several ways — a product, a person, a codename, a version — the first wave settles WHICH entity before any worker researches its history, benchmarks, or controversies: canonical name, first-party URL or account, whether it exists in the claimed category, and a confidence line. An unresolved entity never becomes a premise in a later wave's spawn message; that is exactly how a run starts inventing facts about something that does not exist.
179
181
 
180
182
  Role protocols — embed the relevant one in each spawn message; every worker gets a unique angle:
181
183
 
182
184
  - **Codebase (explore), 2-4 workers.** Grep with 3+ keyword variations; structural/AST search; LSP definitions and references; file-name globs; `git log --all -S '<keyword>'` and `--grep` for history including deleted code. Cross-validate hits across tools. Report absolute file paths, patterns with `file:line`, and how findings connect.
183
185
  - **Web (librarian), 3-6 workers.** At least 10 distinct websearch queries per worker, each with a different operator or angle (see Search craft); fetch the full page for every result that matters — snippets lie. Context7 with 3+ queries per known library. grep.app and `gh search code|repos|issues` for real-world usage. Official docs via sitemap discovery (`<base>/sitemap.xml`), then targeted pages.
184
- - **Browsing, 0-3 workers.** Pages plain fetch cannot read (WAF, 403, Cloudflare, dynamic rendering, login): the worker loads the `ultimate-browsing` skill and escalates through its tiers — Tier-1 insane-search engine first (including its Phase-2.5 archive surrogates), then Tier-2 Chrome stealth — rather than abandoning the source. Capture screenshots when visual context matters. **Provenance is part of the claim**: when a source came back with `provenance` of `snapshot` (an archive copy), cite it with its `snapshot_timestamp` and never state it as the current live page; content from a `proxy` route is `untrusted` and needs a second independent route before any claim rests on it. When one blocked territory hides many leads, fan out more browsing subagents in parallel for breadth instead of serializing one worker through them.
186
+ - **Browsing, 1-3 workers on a `Browsing: yes` brief (0 otherwise).** This worker RENDERS pages, it does not re-fetch them: it drives a real browser through whatever the harness provides — the Browser plugin, an agent-browser or playwright skill, or a code-cell browser — and loads the `ultimate-browsing` skill to escalate through its tiers (insane-search engine with its Phase-2.5 archive surrogates, platform-native readers, then Chrome stealth) only when that browser is blocked. Its standing deliverable is a full-page screenshot of every top source plus the rendered text plain fetch could not reach; a worker that returns only fetched text has not done its job. JS-rendered, login-gated, WAF-blocked, and screenshot-bearing sources all belong here rather than in the web lane. **Provenance is part of the claim**: when a source came back with `provenance` of `snapshot` (an archive copy), cite it with its `snapshot_timestamp` and never state it as the current live page; content from a `proxy` route is `untrusted` and needs a second independent route before any claim rests on it. When one blocked territory hides many leads, fan out more browsing subagents in parallel for breadth instead of serializing one worker through them.
185
187
  - **Repo deep-dive (librarian), 0-2 workers.** Shallow-clone the most relevant repos to `${TMPDIR:-/tmp}`, pin the HEAD SHA, read core modules, follow call chains, return SHA-pinned permalinks.
186
188
 
187
189
  Example spawn (codebase axis; librarian, browsing, and repo-dive follow the same contract with their own protocol):
@@ -381,6 +383,7 @@ High-yield combinations: official docs (`site:<docs domain>`), GitHub implementa
381
383
  | Obeying a surrounding "stop exploring" rule mid-research | Authority section — those rules do not bind this mode |
382
384
  | Asking a worker to write journal or session files | Workers are read-only; you journal every return |
383
385
  | Two workers given the same angle | One unique angle per worker, always |
386
+ | A `Browsing: yes` run whose roster carries no browsing worker | The browsing column is binding — name the angle in the brief and spawn it with the first wave, before any lead is chased |
384
387
  | Contested claim settled by judgment | Phase 3 — run code, capture output, verdict |
385
388
  | Deliverable claims without citations | Every claim cites a source or a verification artifact |
386
389
  | Guessing the deliverable format instead of asking | The format gate is unconditional: propose PDF+DOCX plus the domain-fitting alternatives and the template, then wait before wave 1 |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: visual-qa
3
- description: "MUST USE after building/changing any UI or when asked whether a page, component, or TUI looks right. Rigorous visual QA across web/page, terminal, and paginated-document surfaces. Prefer browser:control-in-app-browser for unauthenticated browser/page QA in Codex, then Playwright/agent-browser/dev-browser. Captures screenshot/TUI evidence with bundled diff scripts, runs design-system/functional and visual-fidelity/CJK reviewer passes, then synthesizes a good/bad verdict. Triggers: visual QA, screenshot/pixel diff, UI looks wrong, reference fidelity, design system check, responsive check, CJK text clipping, TUI alignment, box-drawing drift, PDF page check, deck review, blank or near-empty page, wrong page break."
3
+ description: "Runs rigorous visual QA across web, terminal, and paginated surfaces with screenshot evidence and a verdict. Use for any UI build or change, or when asked whether a page, component, or TUI looks right."
4
4
  ---
5
5
 
6
6
  # Visual QA - Dual-Oracle Web and TUI Verification
@@ -49,7 +49,7 @@ Before any reviewer sees an image, verify each capture yourself: the file signat
49
49
  ### Web
50
50
 
51
51
  1. Capture a REFERENCE image: the user's mock/target, generated page snapshot, Figma export, source-site capture, or known-good baseline. Save as PNG. If the user provided overview text or annotations, save them next to the image and treat them as part of the reference packet.
52
- 2. Capture the ACTUAL rendered screenshot at the same viewport size. In Codex, when `browser:control-in-app-browser` is available and the page does not need an authenticated user browser session, use that Browser plugin first for navigation, page state inspection, and screenshots. If it is unavailable or lacks the needed capture action, use the project's configured browser tooling (the playwright, agent-browser, or dev-browser skill). Save as PNG. If none is configured or available, install [agent-browser](https://github.com/vercel-labs/agent-browser) (`npm install -g agent-browser && agent-browser install`) and capture with it — see `$SKILL_DIR/references/agent-browser-setup.md` for the full setup, including how to shoot a fixed-viewport screenshot.
52
+ 2. Capture the ACTUAL rendered screenshot at the same viewport size, driving the browser your harness actually has. Where a code cell can reach a browser (`new Bun.WebView()` on a Bun >= 1.4 kernel, otherwise `playwright-core` against the local Chrome), capture in-process: it needs no CLI and returns the PNG path directly. In Codex, `browser:control-in-app-browser` is that in-process surface — use it first unless the page needs an authenticated user browser session. Otherwise use the project's configured browser tooling (the playwright, agent-browser, or dev-browser skill). Save as PNG. If nothing is available, install [agent-browser](https://github.com/vercel-labs/agent-browser) (`bun add -g agent-browser && agent-browser install`) and capture with it — see `$SKILL_DIR/references/agent-browser-setup.md` for the full setup, including how to shoot a fixed-viewport screenshot.
53
53
  3. Run the diff and keep the JSON:
54
54
 
55
55
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode",
3
- "version": "5.0.0-beta.40",
3
+ "version": "5.0.0-beta.42",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
5
  "main": "./dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -70,6 +70,8 @@
70
70
  "packages/git-bash-mcp/dist",
71
71
  "packages/shared-skills/package.json",
72
72
  "packages/shared-skills/index.mjs",
73
+ "packages/shared-skills/skill-source-filter.d.ts",
74
+ "packages/shared-skills/skill-source-filter.mjs",
73
75
  "packages/shared-skills/skills",
74
76
  "packages/omo-codex/THIRD-PARTY-NOTICES.md",
75
77
  "packages/omo-codex/marketplace.json",
@@ -122,6 +124,7 @@
122
124
  "build:binaries": "bun run script/build-binaries.ts",
123
125
  "build:schema": "bun run script/build-schema.ts",
124
126
  "build:omo-native": "bun run script/build-omo-native.ts",
127
+ "omob": "bun run script/build-omob.ts",
125
128
  "build:omo-schema": "bun run script/build-omo-schema.ts",
126
129
  "build:model-capabilities": "bun run script/build-model-capabilities.ts",
127
130
  "clean": "rm -rf dist",
@@ -180,7 +183,7 @@
180
183
  "zod": "^4.4.3"
181
184
  },
182
185
  "devDependencies": {
183
- "@code-yeongyu/senpi": "2026.9.4",
186
+ "@code-yeongyu/senpi": "2026.9.4-3",
184
187
  "@oh-my-opencode/agents-md-core": "workspace:*",
185
188
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
186
189
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -221,18 +224,18 @@
221
224
  "typescript": "^7.0.2"
222
225
  },
223
226
  "optionalDependencies": {
224
- "oh-my-opencode-darwin-arm64": "5.0.0-beta.40",
225
- "oh-my-opencode-darwin-x64": "5.0.0-beta.40",
226
- "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.40",
227
- "oh-my-opencode-linux-arm64": "5.0.0-beta.40",
228
- "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.40",
229
- "oh-my-opencode-linux-x64": "5.0.0-beta.40",
230
- "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.40",
231
- "oh-my-opencode-linux-x64-musl": "5.0.0-beta.40",
232
- "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.40",
233
- "oh-my-opencode-windows-arm64": "5.0.0-beta.40",
234
- "oh-my-opencode-windows-x64": "5.0.0-beta.40",
235
- "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.40"
227
+ "oh-my-opencode-darwin-arm64": "5.0.0-beta.42",
228
+ "oh-my-opencode-darwin-x64": "5.0.0-beta.42",
229
+ "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.42",
230
+ "oh-my-opencode-linux-arm64": "5.0.0-beta.42",
231
+ "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.42",
232
+ "oh-my-opencode-linux-x64": "5.0.0-beta.42",
233
+ "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.42",
234
+ "oh-my-opencode-linux-x64-musl": "5.0.0-beta.42",
235
+ "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.42",
236
+ "oh-my-opencode-windows-arm64": "5.0.0-beta.42",
237
+ "oh-my-opencode-windows-x64": "5.0.0-beta.42",
238
+ "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.42"
236
239
  },
237
240
  "overrides": {
238
241
  "@earendil-works/pi-agent-core": "0.84.2",
@@ -73,6 +73,7 @@ export class JsonRpcConnection {
73
73
  let settled = false;
74
74
  const writeCancel = (): Promise<void> => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
75
75
 
76
+ let rejectAfterCancelWrite: (() => Promise<void>) | undefined;
76
77
  const responsePromise = new Promise<T>((resolve, reject) => {
77
78
  const cleanup = (): void => {
78
79
  options.signal?.removeEventListener("abort", onAbort);
@@ -83,15 +84,19 @@ export class JsonRpcConnection {
83
84
  this.pendingRequests.delete(key);
84
85
  cleanup();
85
86
  const rejectCancelled = (): void => reject(abortError(options.signal));
87
+ rejectAfterCancelWrite = async (): Promise<void> => {
88
+ try {
89
+ await writeCancel();
90
+ } catch (error) {
91
+ this.emitError(toError(error));
92
+ }
93
+ rejectCancelled();
94
+ };
86
95
  if (!requestWritten) {
87
96
  cancelAfterWrite = true;
88
- rejectCancelled();
89
97
  return;
90
98
  }
91
- void writeCancel().then(rejectCancelled, (error) => {
92
- this.emitError(toError(error));
93
- rejectCancelled();
94
- });
99
+ void rejectAfterCancelWrite();
95
100
  };
96
101
  const onAbort = (): void => settleCancel();
97
102
  this.pendingRequests.set(key, {
@@ -118,7 +123,7 @@ export class JsonRpcConnection {
118
123
  try {
119
124
  await this.writeMessage(message);
120
125
  requestWritten = true;
121
- if (cancelAfterWrite) await writeCancel();
126
+ if (cancelAfterWrite) await rejectAfterCancelWrite?.();
122
127
  } catch (error) {
123
128
  if (settled) return responsePromise;
124
129
  const pending = this.pendingRequests.get(key);
@@ -686,6 +686,7 @@ class JsonRpcConnection {
686
686
  let cancelAfterWrite = false;
687
687
  let settled = false;
688
688
  const writeCancel = () => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
689
+ let rejectAfterCancelWrite;
689
690
  const responsePromise = new Promise((resolve2, reject) => {
690
691
  const cleanup = () => {
691
692
  options.signal?.removeEventListener("abort", onAbort);
@@ -697,15 +698,19 @@ class JsonRpcConnection {
697
698
  this.pendingRequests.delete(key);
698
699
  cleanup();
699
700
  const rejectCancelled = () => reject(abortError(options.signal));
701
+ rejectAfterCancelWrite = async () => {
702
+ try {
703
+ await writeCancel();
704
+ } catch (error) {
705
+ this.emitError(toError(error));
706
+ }
707
+ rejectCancelled();
708
+ };
700
709
  if (!requestWritten) {
701
710
  cancelAfterWrite = true;
702
- rejectCancelled();
703
711
  return;
704
712
  }
705
- writeCancel().then(rejectCancelled, (error) => {
706
- this.emitError(toError(error));
707
- rejectCancelled();
708
- });
713
+ rejectAfterCancelWrite();
709
714
  };
710
715
  const onAbort = () => settleCancel();
711
716
  this.pendingRequests.set(key, {
@@ -733,7 +738,7 @@ class JsonRpcConnection {
733
738
  await this.writeMessage(message);
734
739
  requestWritten = true;
735
740
  if (cancelAfterWrite)
736
- await writeCancel();
741
+ await rejectAfterCancelWrite?.();
737
742
  } catch (error) {
738
743
  if (settled)
739
744
  return responsePromise;
@@ -1341,6 +1341,7 @@ class JsonRpcConnection {
1341
1341
  let cancelAfterWrite = false;
1342
1342
  let settled = false;
1343
1343
  const writeCancel = () => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
1344
+ let rejectAfterCancelWrite;
1344
1345
  const responsePromise = new Promise((resolve2, reject) => {
1345
1346
  const cleanup = () => {
1346
1347
  options.signal?.removeEventListener("abort", onAbort);
@@ -1352,15 +1353,19 @@ class JsonRpcConnection {
1352
1353
  this.pendingRequests.delete(key);
1353
1354
  cleanup();
1354
1355
  const rejectCancelled = () => reject(abortError2(options.signal));
1356
+ rejectAfterCancelWrite = async () => {
1357
+ try {
1358
+ await writeCancel();
1359
+ } catch (error) {
1360
+ this.emitError(toError(error));
1361
+ }
1362
+ rejectCancelled();
1363
+ };
1355
1364
  if (!requestWritten) {
1356
1365
  cancelAfterWrite = true;
1357
- rejectCancelled();
1358
1366
  return;
1359
1367
  }
1360
- writeCancel().then(rejectCancelled, (error) => {
1361
- this.emitError(toError(error));
1362
- rejectCancelled();
1363
- });
1368
+ rejectAfterCancelWrite();
1364
1369
  };
1365
1370
  const onAbort = () => settleCancel();
1366
1371
  this.pendingRequests.set(key, {
@@ -1388,7 +1393,7 @@ class JsonRpcConnection {
1388
1393
  await this.writeMessage(message);
1389
1394
  requestWritten = true;
1390
1395
  if (cancelAfterWrite)
1391
- await writeCancel();
1396
+ await rejectAfterCancelWrite?.();
1392
1397
  } catch (error) {
1393
1398
  if (settled)
1394
1399
  return responsePromise;
@@ -183,6 +183,7 @@ class JsonRpcConnection {
183
183
  let cancelAfterWrite = false;
184
184
  let settled = false;
185
185
  const writeCancel = () => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
186
+ let rejectAfterCancelWrite;
186
187
  const responsePromise = new Promise((resolve, reject) => {
187
188
  const cleanup = () => {
188
189
  options.signal?.removeEventListener("abort", onAbort);
@@ -194,15 +195,19 @@ class JsonRpcConnection {
194
195
  this.pendingRequests.delete(key);
195
196
  cleanup();
196
197
  const rejectCancelled = () => reject(abortError(options.signal));
198
+ rejectAfterCancelWrite = async () => {
199
+ try {
200
+ await writeCancel();
201
+ } catch (error) {
202
+ this.emitError(toError(error));
203
+ }
204
+ rejectCancelled();
205
+ };
197
206
  if (!requestWritten) {
198
207
  cancelAfterWrite = true;
199
- rejectCancelled();
200
208
  return;
201
209
  }
202
- writeCancel().then(rejectCancelled, (error) => {
203
- this.emitError(toError(error));
204
- rejectCancelled();
205
- });
210
+ rejectAfterCancelWrite();
206
211
  };
207
212
  const onAbort = () => settleCancel();
208
213
  this.pendingRequests.set(key, {
@@ -230,7 +235,7 @@ class JsonRpcConnection {
230
235
  await this.writeMessage(message);
231
236
  requestWritten = true;
232
237
  if (cancelAfterWrite)
233
- await writeCancel();
238
+ await rejectAfterCancelWrite?.();
234
239
  } catch (error) {
235
240
  if (settled)
236
241
  return responsePromise;
@@ -185,6 +185,7 @@ class JsonRpcConnection {
185
185
  let cancelAfterWrite = false;
186
186
  let settled = false;
187
187
  const writeCancel = () => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
188
+ let rejectAfterCancelWrite;
188
189
  const responsePromise = new Promise((resolve, reject) => {
189
190
  const cleanup = () => {
190
191
  options.signal?.removeEventListener("abort", onAbort);
@@ -196,15 +197,19 @@ class JsonRpcConnection {
196
197
  this.pendingRequests.delete(key);
197
198
  cleanup();
198
199
  const rejectCancelled = () => reject(abortError(options.signal));
200
+ rejectAfterCancelWrite = async () => {
201
+ try {
202
+ await writeCancel();
203
+ } catch (error) {
204
+ this.emitError(toError(error));
205
+ }
206
+ rejectCancelled();
207
+ };
199
208
  if (!requestWritten) {
200
209
  cancelAfterWrite = true;
201
- rejectCancelled();
202
210
  return;
203
211
  }
204
- writeCancel().then(rejectCancelled, (error) => {
205
- this.emitError(toError(error));
206
- rejectCancelled();
207
- });
212
+ rejectAfterCancelWrite();
208
213
  };
209
214
  const onAbort = () => settleCancel();
210
215
  this.pendingRequests.set(key, {
@@ -232,7 +237,7 @@ class JsonRpcConnection {
232
237
  await this.writeMessage(message);
233
238
  requestWritten = true;
234
239
  if (cancelAfterWrite)
235
- await writeCancel();
240
+ await rejectAfterCancelWrite?.();
236
241
  } catch (error) {
237
242
  if (settled)
238
243
  return responsePromise;
@@ -183,6 +183,7 @@ class JsonRpcConnection {
183
183
  let cancelAfterWrite = false;
184
184
  let settled = false;
185
185
  const writeCancel = () => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
186
+ let rejectAfterCancelWrite;
186
187
  const responsePromise = new Promise((resolve, reject) => {
187
188
  const cleanup = () => {
188
189
  options.signal?.removeEventListener("abort", onAbort);
@@ -194,15 +195,19 @@ class JsonRpcConnection {
194
195
  this.pendingRequests.delete(key);
195
196
  cleanup();
196
197
  const rejectCancelled = () => reject(abortError(options.signal));
198
+ rejectAfterCancelWrite = async () => {
199
+ try {
200
+ await writeCancel();
201
+ } catch (error) {
202
+ this.emitError(toError(error));
203
+ }
204
+ rejectCancelled();
205
+ };
197
206
  if (!requestWritten) {
198
207
  cancelAfterWrite = true;
199
- rejectCancelled();
200
208
  return;
201
209
  }
202
- writeCancel().then(rejectCancelled, (error) => {
203
- this.emitError(toError(error));
204
- rejectCancelled();
205
- });
210
+ rejectAfterCancelWrite();
206
211
  };
207
212
  const onAbort = () => settleCancel();
208
213
  this.pendingRequests.set(key, {
@@ -230,7 +235,7 @@ class JsonRpcConnection {
230
235
  await this.writeMessage(message);
231
236
  requestWritten = true;
232
237
  if (cancelAfterWrite)
233
- await writeCancel();
238
+ await rejectAfterCancelWrite?.();
234
239
  } catch (error) {
235
240
  if (settled)
236
241
  return responsePromise;
@@ -687,6 +687,7 @@ class JsonRpcConnection {
687
687
  let cancelAfterWrite = false;
688
688
  let settled = false;
689
689
  const writeCancel = () => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
690
+ let rejectAfterCancelWrite;
690
691
  const responsePromise = new Promise((resolve2, reject) => {
691
692
  const cleanup = () => {
692
693
  options.signal?.removeEventListener("abort", onAbort);
@@ -698,15 +699,19 @@ class JsonRpcConnection {
698
699
  this.pendingRequests.delete(key);
699
700
  cleanup();
700
701
  const rejectCancelled = () => reject(abortError(options.signal));
702
+ rejectAfterCancelWrite = async () => {
703
+ try {
704
+ await writeCancel();
705
+ } catch (error) {
706
+ this.emitError(toError(error));
707
+ }
708
+ rejectCancelled();
709
+ };
701
710
  if (!requestWritten) {
702
711
  cancelAfterWrite = true;
703
- rejectCancelled();
704
712
  return;
705
713
  }
706
- writeCancel().then(rejectCancelled, (error) => {
707
- this.emitError(toError(error));
708
- rejectCancelled();
709
- });
714
+ rejectAfterCancelWrite();
710
715
  };
711
716
  const onAbort = () => settleCancel();
712
717
  this.pendingRequests.set(key, {
@@ -734,7 +739,7 @@ class JsonRpcConnection {
734
739
  await this.writeMessage(message);
735
740
  requestWritten = true;
736
741
  if (cancelAfterWrite)
737
- await writeCancel();
742
+ await rejectAfterCancelWrite?.();
738
743
  } catch (error) {
739
744
  if (settled)
740
745
  return responsePromise;
@@ -375,6 +375,7 @@ class JsonRpcConnection {
375
375
  let cancelAfterWrite = false;
376
376
  let settled = false;
377
377
  const writeCancel = () => this.writeMessage({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } });
378
+ let rejectAfterCancelWrite;
378
379
  const responsePromise = new Promise((resolve2, reject) => {
379
380
  const cleanup = () => {
380
381
  options.signal?.removeEventListener("abort", onAbort);
@@ -386,15 +387,19 @@ class JsonRpcConnection {
386
387
  this.pendingRequests.delete(key);
387
388
  cleanup();
388
389
  const rejectCancelled = () => reject(abortError(options.signal));
390
+ rejectAfterCancelWrite = async () => {
391
+ try {
392
+ await writeCancel();
393
+ } catch (error) {
394
+ this.emitError(toError(error));
395
+ }
396
+ rejectCancelled();
397
+ };
389
398
  if (!requestWritten) {
390
399
  cancelAfterWrite = true;
391
- rejectCancelled();
392
400
  return;
393
401
  }
394
- writeCancel().then(rejectCancelled, (error) => {
395
- this.emitError(toError(error));
396
- rejectCancelled();
397
- });
402
+ rejectAfterCancelWrite();
398
403
  };
399
404
  const onAbort = () => settleCancel();
400
405
  this.pendingRequests.set(key, {
@@ -422,7 +427,7 @@ class JsonRpcConnection {
422
427
  await this.writeMessage(message);
423
428
  requestWritten = true;
424
429
  if (cancelAfterWrite)
425
- await writeCancel();
430
+ await rejectAfterCancelWrite?.();
426
431
  } catch (error) {
427
432
  if (settled)
428
433
  return responsePromise;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omo",
3
- "version": "5.0.0-beta.40",
3
+ "version": "5.0.0-beta.42",
4
4
  "description": "One Codex plugin namespace for Yeongyu's local Codex components.",
5
5
  "author": {
6
6
  "name": "Yeongyu Kim",
@@ -8,7 +8,7 @@
8
8
  "command": "node \"${PLUGIN_ROOT}/components/bootstrap/dist/cli.js\" hook session-start",
9
9
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\bootstrap.ps1\"",
10
10
  "timeout": 30,
11
- "statusMessage": "(OmO 5.0.0-beta.40) Checking Bootstrap Provisioning"
11
+ "statusMessage": "(OmO 5.0.0-beta.42) Checking Bootstrap Provisioning"
12
12
  }
13
13
  ]
14
14
  }