oh-my-opencode 5.0.0-beta.81 → 5.0.0-beta.83

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 (132) hide show
  1. package/README.ja.md +2 -2
  2. package/README.ko.md +2 -2
  3. package/README.md +4 -4
  4. package/README.ru.md +2 -2
  5. package/README.zh-cn.md +2 -2
  6. package/dist/cli/index.js +439 -98
  7. package/dist/cli/install-native/index.d.ts +4 -0
  8. package/dist/cli/install-native/plan.d.ts +11 -0
  9. package/dist/cli/install-native/run-native-install.d.ts +23 -0
  10. package/dist/cli/install-native-dev/index.d.ts +4 -0
  11. package/dist/cli/native-dev-platform-flag.d.ts +5 -0
  12. package/dist/cli/native-edition-hint.d.ts +10 -0
  13. package/dist/cli/types.d.ts +3 -2
  14. package/dist/cli-node/index.js +439 -98
  15. package/dist/config/schema/hooks.d.ts +1 -0
  16. package/dist/config-migration/harness-native-rename.d.ts +3 -0
  17. package/dist/create-hooks.d.ts +1 -0
  18. package/dist/features/native-edition-nudge/index.d.ts +2 -0
  19. package/dist/features/native-edition-nudge/tui.d.ts +33 -0
  20. package/dist/hooks/index.d.ts +1 -0
  21. package/dist/hooks/native-edition-nudge/decide.d.ts +4 -0
  22. package/dist/hooks/native-edition-nudge/hook.d.ts +26 -0
  23. package/dist/hooks/native-edition-nudge/index.d.ts +5 -0
  24. package/dist/hooks/native-edition-nudge/state.d.ts +9 -0
  25. package/dist/hooks/native-edition-nudge/types.d.ts +38 -0
  26. package/dist/index.js +753 -352
  27. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
  28. package/dist/plugin/hooks/create-session-hooks.d.ts +2 -1
  29. package/dist/skills/browser/ATTRIBUTION.md +25 -0
  30. package/dist/skills/browser/SKILL.md +89 -0
  31. package/dist/skills/browser/references/commands.md +90 -0
  32. package/dist/skills/browser/references/install.md +71 -0
  33. package/dist/skills/browser/references/owned-engine/README.md +42 -0
  34. package/dist/skills/browser/references/owned-engine/frames-and-humans.md +39 -0
  35. package/dist/skills/browser/references/owned-engine/ladder.md +59 -0
  36. package/dist/skills/browser/references/owned-engine/network.md +42 -0
  37. package/dist/skills/browser/references/recipes/1password.md +59 -0
  38. package/dist/skills/browser/references/remote.md +24 -0
  39. package/dist/skills/browser/scripts/browser-doctor.mjs +41 -0
  40. package/dist/skills/browser/scripts/browser-env.mjs +41 -0
  41. package/dist/skills/browser/scripts/browser-install.mjs +51 -0
  42. package/dist/tui.js +545 -67
  43. package/package.json +17 -15
  44. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  45. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +111 -11
  46. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  47. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  48. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  49. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  50. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  51. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  52. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  53. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  54. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  55. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  56. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  58. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  59. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  60. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  62. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  63. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  64. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  65. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  66. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  67. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +24 -10
  68. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +8 -6
  69. package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.d.ts +2 -0
  70. package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.js +21 -4
  71. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  72. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  73. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +10 -5
  74. package/packages/omo-codex/plugin/components/ulw-loop/src/surface.ts +24 -6
  75. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard-native-reviewer.test.ts +207 -0
  76. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +6 -4
  77. package/packages/omo-codex/plugin/components/ulw-loop/test/surface.test.ts +43 -4
  78. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  80. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  81. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  82. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  83. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  84. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  85. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  86. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  87. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  88. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  89. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  90. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  91. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  92. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  93. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  94. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  95. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  99. package/packages/omo-codex/plugin/package-lock.json +12 -12
  100. package/packages/omo-codex/plugin/package.json +1 -1
  101. package/packages/omo-codex/plugin/skills/browser/ATTRIBUTION.md +25 -0
  102. package/packages/omo-codex/plugin/skills/browser/SKILL.md +89 -0
  103. package/packages/omo-codex/plugin/skills/browser/agents/openai.yaml +2 -0
  104. package/packages/omo-codex/plugin/skills/browser/references/commands.md +90 -0
  105. package/packages/omo-codex/plugin/skills/browser/references/install.md +71 -0
  106. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/README.md +42 -0
  107. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/frames-and-humans.md +39 -0
  108. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/ladder.md +59 -0
  109. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/network.md +42 -0
  110. package/packages/omo-codex/plugin/skills/browser/references/recipes/1password.md +59 -0
  111. package/packages/omo-codex/plugin/skills/browser/references/remote.md +24 -0
  112. package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +41 -0
  113. package/packages/omo-codex/plugin/skills/browser/scripts/browser-env.mjs +41 -0
  114. package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +51 -0
  115. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +1 -0
  116. package/packages/omo-codex/scripts/install-dist/install-local.mjs +117 -17
  117. package/packages/shared-skills/skills/browser/ATTRIBUTION.md +25 -0
  118. package/packages/shared-skills/skills/browser/SKILL.md +89 -0
  119. package/packages/shared-skills/skills/browser/references/commands.md +90 -0
  120. package/packages/shared-skills/skills/browser/references/install.md +71 -0
  121. package/packages/shared-skills/skills/browser/references/owned-engine/README.md +42 -0
  122. package/packages/shared-skills/skills/browser/references/owned-engine/frames-and-humans.md +39 -0
  123. package/packages/shared-skills/skills/browser/references/owned-engine/ladder.md +59 -0
  124. package/packages/shared-skills/skills/browser/references/owned-engine/network.md +42 -0
  125. package/packages/shared-skills/skills/browser/references/recipes/1password.md +59 -0
  126. package/packages/shared-skills/skills/browser/references/remote.md +24 -0
  127. package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +41 -0
  128. package/packages/shared-skills/skills/browser/scripts/browser-env.mjs +41 -0
  129. package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +51 -0
  130. package/postinstall.mjs +3 -0
  131. package/dist/cli/install-senpi/index.d.ts +0 -1
  132. package/dist/cli/senpi-platform-flag.d.ts +0 -4
@@ -0,0 +1,59 @@
1
+ # The escalation ladder
2
+
3
+ **Two identical failures on the same target mean climb, not retry. A third identical attempt is a
4
+ defect.** The failure itself selects the next rung; never pause to ask which.
5
+
6
+ | Rung | Use | Climb when |
7
+ |---|---|---|
8
+ | 1. snapshot + locator(ref) | anything with a usable ref | the ref is absent, stale, obscured, or the click lands on the wrong node twice |
9
+ | 1b. layer description | a blocking overlay explains two identical misses | no overlay is reported, or it is gone and the click still misses |
10
+ | 2. coordinates | canvas, extension popups, custom controls, drag surfaces | the click misses, or the screenshot and the coordinates disagree |
11
+ | 3. pin the viewport, redo rung 2 | coordinate drift after a resize, a DPI change, or a foreign tab | coordinates land correctly but the widget still refuses input |
12
+ | 4. the challenge widget | a challenge is the blocker | it clears and the flow still stalls |
13
+ | 5. read the browser log | a browser-level failure | the log names a cause outside the page |
14
+
15
+ **Rung 5 ends in a written diagnosis, never a speculative code change.** A missing entitlement, a
16
+ dead extension service worker, an unavailable authenticator — none of those is fixed by editing
17
+ automation code.
18
+
19
+ ## Pin the viewport before trusting any coordinate
20
+
21
+ Coordinate input acts in viewport pixels. When the render surface and the coordinate space
22
+ disagree, every coordinate is off by the same constant and retrying just repeats the miss. Set the
23
+ device metrics explicitly (width, height, `deviceScaleFactor`, and a matching viewport), re-read
24
+ the page's viewport size, then take a **fresh** screenshot. Coordinates read off an unpinned
25
+ screenshot are stale. Pin every page you act on, including tabs you did not open.
26
+
27
+ ## Refs
28
+
29
+ - Refs die on every new snapshot. Pass a ref straight from the latest snapshot; never reuse one
30
+ across snapshots and never put one in a CSS selector.
31
+ - Refs stay page-level. A child-frame ref still resolves through the page, so you never fetch a
32
+ frame handle to click inside an iframe.
33
+ - Always compact a snapshot before sending it to a model.
34
+
35
+ ## Challenge widgets
36
+
37
+ A challenge is an obstacle on the path, not a stop sign: clear it, confirm it cleared, continue.
38
+ Three shapes, three techniques:
39
+
40
+ - **Checkbox** (the common embedded widgets): the control lives in a nested frame, so address it
41
+ by viewport coordinates.
42
+ - **Slider / puzzle**: drag along a path with enough intermediate steps that the motion reads as
43
+ human. Raise the step count before you change the endpoints.
44
+ - **Text or number**: OCR the region. On macOS the system vision framework is the cheapest
45
+ accurate option; elsewhere pass your own OCR function or a vision model.
46
+ - **Image grid**: annotate a screenshot, then click per cell.
47
+
48
+ **Bounds measured against an unpinned viewport are wrong by a constant offset.** If clicks have
49
+ been landing wrong, drop to rung 3 first, then re-read the bounds.
50
+
51
+ Verify with a fresh snapshot that the challenge is gone. Verification is part of the action, not a
52
+ separate optimistic assumption.
53
+
54
+ ## Delegating the pixel loop
55
+
56
+ Coordinate work and challenge solving are iterative: screenshot, reason, act, verify. When that
57
+ loop would consume your own context, delegate the blocked page to a subagent armed with these
58
+ references and have it return the post-solve state. Drive it yourself when the flow is short or
59
+ the state is already in your hands.
@@ -0,0 +1,42 @@
1
+ # Read the network, not the DOM
2
+
3
+ When a page renders a list, a table, or search results, the data almost always arrived as JSON one
4
+ request earlier. That JSON is complete, typed, free of markup, and immune to the layout changing
5
+ next week. Watching traffic costs nothing on a target you own, and the page cannot observe it.
6
+
7
+ **Snoop before you scrape.** Scroll-and-parse is the fallback, not the default.
8
+
9
+ ## Wait for a request, never for a clock
10
+
11
+ A fixed sleep is a guess that fails on a slower machine and wastes time on a faster one. Subscribe
12
+ to the response you expect **before** triggering the action, then await that signal with a bounded
13
+ timeout. This is the same rule that governs test code, for the same reason.
14
+
15
+ ## Collecting through infinite scroll
16
+
17
+ Drive scroll and extraction as a generator: scroll, collect what is new, stop on a target count or
18
+ a scroll ceiling. Bound both, so a page that keeps producing cannot run forever.
19
+
20
+ ## Flight traces
21
+
22
+ For QA evidence, record a trace: one entry per step with before/after screenshots, the network
23
+ log, and console output, written as a line-delimited log plus an HAR. That triple is what makes a
24
+ failure reconstructable afterwards instead of re-runnable-in-theory.
25
+
26
+ Cap recorded body sizes. An untrimmed trace of a media-heavy page is mostly bytes nobody reads.
27
+
28
+ ## Request interception
29
+
30
+ Route matching by glob, regular expression, or predicate lets you stub an endpoint, fail one
31
+ request to test an error path, or block third-party noise. Enable interception on the first route
32
+ and disable it on dispose — leaving it on slows every later navigation.
33
+
34
+ ## Cookies
35
+
36
+ Injecting a cookie export into your own profile is legitimate for ordinary sessions and useless
37
+ for the ones you most want: accounts whose risk engines bind a session to a device will invalidate
38
+ it, and a password manager's session is tab-bound and never portable. Sanitize what you do inject
39
+ (drop expired entries, keep host-prefixed cookies secure and root-scoped).
40
+
41
+ **Never copy a session out of the user's real browser profile.** If you need their login, that is
42
+ the attached engine's job.
@@ -0,0 +1,59 @@
1
+ # Reading a 1Password vault the user has unlocked
2
+
3
+ Scope: reading an item out of a vault **the user has already unlocked in their browser**. Signing
4
+ in is the user's job; see "Not in scope" below.
5
+
6
+ ## The fact that shapes everything
7
+
8
+ 1Password web is zero-knowledge. The unlocked vault key lives in **one tab's `sessionStorage`**,
9
+ not in a cookie. So:
10
+
11
+ - A **new** tab pointed at the vault redirects to the sign-in page even while a signed-in tab sits
12
+ right beside it. Opening your own tab does not work and never will.
13
+ - Navigating the authenticated tab away, or closing it, **destroys the session** until a human
14
+ signs in again.
15
+
16
+ Therefore: borrow the existing tab, read, return it. Do not open, do not navigate away, do not
17
+ "refresh to fix" anything.
18
+
19
+ ```bash
20
+ bsk tab list --scope user # find the already-signed-in tab
21
+ bsk tab borrow <tab-id>
22
+ # ... read ...
23
+ bsk tab return <tab-id>
24
+ ```
25
+
26
+ ## Check liveness by URL, never by title
27
+
28
+ The app mutates its URL hash without updating the document title, so a signed-in vault can still
29
+ report a sign-in title. A live session's `location.href` contains the app path and never the
30
+ sign-in path. Read the URL.
31
+
32
+ ## The browser may be shared
33
+
34
+ Other automation may be driving the same window.
35
+
36
+ - **Clear the search box before typing.** It often holds someone else's query, and typing alone
37
+ appends to it, returning confident results for the wrong search.
38
+ - **Read the autocomplete listbox, not the results grid.** The grid holds whatever query was last
39
+ committed, possibly by another actor. Scraping every option on the page silently merges two
40
+ different searches.
41
+ - **Leave the tab on the item list** when you are done, so the next actor starts clean.
42
+
43
+ ## If it is locked
44
+
45
+ ```bash
46
+ bsk request-help --session <id> --prompt "Please unlock 1Password in this tab, then continue."
47
+ ```
48
+
49
+ Then observe again.
50
+
51
+ ## Not in scope
52
+
53
+ - **Do not type a master password, and do not automate the sign-in form.** Only the account owner
54
+ signs in. If the vault is locked, hand it to them.
55
+ - **Do not extract secrets through the page** beyond the single value the user asked for, and
56
+ report it masked (`abcd…9fb0 (len 37)`) unless they asked for the exact string.
57
+ - **Do not copy, inject, or reuse session cookies.** The session is device- and tab-bound; cookie
58
+ reuse cannot work and trips account protections.
59
+ - Clear the clipboard if you ever populate it.
@@ -0,0 +1,24 @@
1
+ # Agent here, browser there
2
+
3
+ The agent and the browser do not have to be the same machine. Upstream supports this directly;
4
+ nothing in this package needs to bridge it.
5
+
6
+ Shape:
7
+
8
+ - The **browser machine** needs only the extension.
9
+ - The **agent machine** runs the `bsk` CLI and this skill.
10
+ - They are joined by a daemon in server mode plus a pairing link.
11
+
12
+ ```bash
13
+ bsk daemon start --mode server # on the machine the agent runs on
14
+ ```
15
+
16
+ Then follow upstream's pairing guide, which owns the current flag surface and the TLS
17
+ prerequisites: https://github.com/Tencent/BrowserSkill/blob/main/docs/remote-extension-connection.md
18
+
19
+ Once paired, every command in `commands.md` behaves identically; `--out` paths still resolve on
20
+ the machine that ran the command.
21
+
22
+ If pairing is not configured, say so and ask. Do not substitute a browser on the agent machine:
23
+ the point of the attached engine is the sessions that live in the user's browser, and a local
24
+ browser has none of them.
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ import { platform } from "node:os"
3
+ import { connectedBrowsers, readStatus, resolveCli, STORE_LISTINGS, SUPPORTED_PLATFORMS } from "./browser-env.mjs"
4
+
5
+ const REMEDIES = {
6
+ ready: "Start a session: bsk session start --json --no-focus --name \"<task>\"",
7
+ "no-extension": `Ask the user to install and enable the extension, then re-run this doctor.\n Chrome: ${STORE_LISTINGS.chrome}\n Edge: ${STORE_LISTINGS.edge}`,
8
+ "no-cli": "Install the CLI: node \"<skill-root>/scripts/browser-install.mjs\", then re-run this doctor.",
9
+ "no-browser-support": "This platform has no supported browser. Say so and stop; do not substitute another engine.",
10
+ }
11
+
12
+ async function diagnose() {
13
+ if (!SUPPORTED_PLATFORMS.has(platform())) {
14
+ return { state: "no-browser-support", platform: platform() }
15
+ }
16
+ const cli = await resolveCli()
17
+ if (cli === undefined) return { state: "no-cli", platform: platform() }
18
+
19
+ let status
20
+ try {
21
+ status = await readStatus(cli)
22
+ } catch (error) {
23
+ return { state: "no-extension", platform: platform(), cli, detail: `status failed: ${error.message}` }
24
+ }
25
+ const browsers = connectedBrowsers(status)
26
+ if (browsers.length === 0) return { state: "no-extension", platform: platform(), cli }
27
+ return { state: "ready", platform: platform(), cli, browsers: browsers.length }
28
+ }
29
+
30
+ const report = await diagnose()
31
+ const json = process.argv.includes("--json")
32
+ if (json) {
33
+ console.log(JSON.stringify({ ...report, remedy: REMEDIES[report.state] }, null, 2))
34
+ } else {
35
+ console.log(`state: ${report.state}`)
36
+ if (report.cli) console.log(`cli: ${report.cli}`)
37
+ if (report.browsers) console.log(`browsers connected: ${report.browsers}`)
38
+ if (report.detail) console.log(`detail: ${report.detail}`)
39
+ console.log(`\n${REMEDIES[report.state]}`)
40
+ }
41
+ process.exit(report.state === "ready" ? 0 : 1)
@@ -0,0 +1,41 @@
1
+ import { execFile } from "node:child_process"
2
+ import { existsSync } from "node:fs"
3
+ import { homedir, platform } from "node:os"
4
+ import { join } from "node:path"
5
+ import { promisify } from "node:util"
6
+
7
+ const run = promisify(execFile)
8
+
9
+ export const STORE_LISTINGS = {
10
+ chrome: "https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi",
11
+ edge: "https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg",
12
+ }
13
+
14
+ export const SUPPORTED_PLATFORMS = new Set(["darwin", "linux", "win32"])
15
+
16
+ export function localBinCandidates() {
17
+ const bin = join(homedir(), ".local", "bin")
18
+ return platform() === "win32" ? [join(bin, "bsk.exe"), join(bin, "bsk")] : [join(bin, "bsk")]
19
+ }
20
+
21
+ export async function resolveCli() {
22
+ for (const candidate of localBinCandidates()) {
23
+ if (existsSync(candidate)) return candidate
24
+ }
25
+ try {
26
+ await run("bsk", ["--version"], { timeout: 15000, windowsHide: true })
27
+ return "bsk"
28
+ } catch {
29
+ return undefined
30
+ }
31
+ }
32
+
33
+ export async function readStatus(cli) {
34
+ const { stdout } = await run(cli, ["status", "--json"], { timeout: 30000, windowsHide: true })
35
+ return JSON.parse(stdout)
36
+ }
37
+
38
+ export function connectedBrowsers(status) {
39
+ const browsers = status?.browsers
40
+ return Array.isArray(browsers) ? browsers : []
41
+ }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process"
3
+ import { platform } from "node:os"
4
+ import { resolveCli, STORE_LISTINGS, SUPPORTED_PLATFORMS } from "./browser-env.mjs"
5
+
6
+ const UNIX_INSTALL = "curl -fsSL https://raw.githubusercontent.com/Tencent/BrowserSkill/main/install.sh | sh"
7
+ const WINDOWS_INSTALL = "irm https://raw.githubusercontent.com/Tencent/BrowserSkill/main/install.ps1 | iex"
8
+
9
+ function installCommand() {
10
+ return platform() === "win32"
11
+ ? { file: "powershell", args: ["-NoProfile", "-Command", WINDOWS_INSTALL], display: WINDOWS_INSTALL }
12
+ : { file: "sh", args: ["-c", UNIX_INSTALL], display: UNIX_INSTALL }
13
+ }
14
+
15
+ function spawnInstaller({ file, args }) {
16
+ return new Promise((resolve) => {
17
+ const child = spawn(file, args, { stdio: "inherit", windowsHide: true })
18
+ child.on("error", () => resolve(1))
19
+ child.on("close", (code) => resolve(code ?? 1))
20
+ })
21
+ }
22
+
23
+ if (!SUPPORTED_PLATFORMS.has(platform())) {
24
+ console.error(`unsupported platform: ${platform()}. macOS, Linux and Windows x64 only.`)
25
+ process.exit(1)
26
+ }
27
+
28
+ if (await resolveCli()) {
29
+ console.log("bsk is already installed; run browser-doctor.mjs to check the extension.")
30
+ process.exit(0)
31
+ }
32
+
33
+ const command = installCommand()
34
+ console.log(`installing the bsk CLI with the upstream installer:\n ${command.display}\n`)
35
+ const code = await spawnInstaller(command)
36
+ if (code !== 0) {
37
+ console.error(`\ninstaller exited ${code}. Report this instead of retrying blindly.`)
38
+ process.exit(code)
39
+ }
40
+
41
+ console.log([
42
+ "",
43
+ "CLI installed. Two things remain, and only the user can do the first:",
44
+ "",
45
+ "1. Install the browser extension from the store, then enable it:",
46
+ ` Chrome: ${STORE_LISTINGS.chrome}`,
47
+ ` Edge: ${STORE_LISTINGS.edge}`,
48
+ "2. Re-run: node \"<skill-root>/scripts/browser-doctor.mjs\"",
49
+ "",
50
+ "If PATH does not pick up the CLI in this shell, use its absolute path under ~/.local/bin.",
51
+ ].join("\n"))
@@ -6,6 +6,7 @@ export const CONTEXT_PRESSURE_SKILL_BUDGET_BYTES = 25_000;
6
6
 
7
7
  export const expectedSkills = [
8
8
  "ast-grep",
9
+ "browser",
9
10
  "coding-agent-sessions",
10
11
  "comment-checker",
11
12
  "data-scientist",
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // omo-codex-install:6df5dbbbb7ca9b9dce86fae98a525a9caa7de96caed85a4b77d4e8b4ab4d1501:fea0803f89d0a4a013652e8905ddb0c30456affbd1a7b833eb9092f49d9566dc
2
+ // omo-codex-install:88ab1eaf77d9f58468ed3f04e9c7d6c7dbb32d7c87feee69f45401431352184f:6f7e0588dc9a653cf42c9b4466272eea05e8c081bc8dc62082c03c1e5562cc31
3
3
  var __esm = (fn, res, err) => () => {
4
4
  if (fn)
5
5
  try {
@@ -316,7 +316,7 @@ function shouldRetainLine(line, cutoffMs) {
316
316
  function parseDiagnosticLine(line) {
317
317
  try {
318
318
  const parsed = JSON.parse(line);
319
- if (!isRecord9(parsed)) {
319
+ if (!isRecord10(parsed)) {
320
320
  return null;
321
321
  }
322
322
  return parsed;
@@ -327,7 +327,7 @@ function parseDiagnosticLine(line) {
327
327
  throw error;
328
328
  }
329
329
  }
330
- function isRecord9(value) {
330
+ function isRecord10(value) {
331
331
  return value !== null && typeof value === "object" && !Array.isArray(value);
332
332
  }
333
333
  function trimToMaxBytes(lines) {
@@ -7817,7 +7817,7 @@ function coerceBool(value) {
7817
7817
  function coerceString(value) {
7818
7818
  return typeof value == "string" ? value : undefined;
7819
7819
  }
7820
- function isRecord10(value) {
7820
+ function isRecord11(value) {
7821
7821
  return typeof value == "object" && value !== null && !Array.isArray(value);
7822
7822
  }
7823
7823
  function toRfc3339(timestamp) {
@@ -7839,7 +7839,7 @@ function relocateInto(properties, key, value) {
7839
7839
  properties[key] = value;
7840
7840
  }
7841
7841
  function buildV1Event(message) {
7842
- const sourceProperties = isRecord10(message.properties) ? message.properties : {};
7842
+ const sourceProperties = isRecord11(message.properties) ? message.properties : {};
7843
7843
  const properties = {
7844
7844
  ...sourceProperties
7845
7845
  };
@@ -9984,7 +9984,7 @@ var package_default;
9984
9984
  var init_package = __esm(() => {
9985
9985
  package_default = {
9986
9986
  name: "@oh-my-opencode/omo-codex",
9987
- version: "5.0.0-beta.81",
9987
+ version: "5.0.0-beta.83",
9988
9988
  type: "module",
9989
9989
  private: true,
9990
9990
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -16515,8 +16515,10 @@ var OmoGitMasterSettingsSchema = OmoGitMasterSettingsLayerSchema.extend({
16515
16515
 
16516
16516
  // packages/omo-config-core/src/schema/harness.ts
16517
16517
  var HARNESS_IDS = ["codex", "opencode", "omo"];
16518
- var OMO_CONFIG_HARNESS_IDS = ["opencode", "senpi", "codex"];
16518
+ var OMO_CONFIG_HARNESS_IDS = ["opencode", "native", "codex"];
16519
16519
  var OmoHarnessIdSchema = _enum(OMO_CONFIG_HARNESS_IDS);
16520
+ var OMO_CONFIG_LEGACY_HARNESS_ALIASES = { senpi: "native" };
16521
+ var OMO_CONFIG_LEGACY_HARNESS_IDS = Object.keys(OMO_CONFIG_LEGACY_HARNESS_ALIASES);
16520
16522
 
16521
16523
  // packages/omo-config-core/src/schema/memory.ts
16522
16524
  var OmoMemoryReflectionTriggerSchema = object({
@@ -16758,6 +16760,31 @@ var OmoTaskTeamSettingsSchema = object({
16758
16760
  var OmoTaskWarningsSchema = object({
16759
16761
  unavailable_categories: boolean2().default(true)
16760
16762
  }).strict();
16763
+ var IsolationBackendKindSchema = _enum([
16764
+ "auto",
16765
+ "apfs",
16766
+ "btrfs",
16767
+ "zfs",
16768
+ "reflink",
16769
+ "overlayfs",
16770
+ "block-clone",
16771
+ "rcopy"
16772
+ ]);
16773
+ var OmoTaskIsolationSchema = object({
16774
+ enabled: boolean2().default(false),
16775
+ backend: IsolationBackendKindSchema.default("auto"),
16776
+ apply: boolean2().default(true),
16777
+ merge: _enum(["patch", "branch"]).default("patch"),
16778
+ commits: _enum(["generic", "ai"]).default("generic")
16779
+ }).strict();
16780
+ var isolationDefaults = OmoTaskIsolationSchema.parse({});
16781
+ var OmoTaskIsolationLayerSchema = object({
16782
+ enabled: boolean2().optional(),
16783
+ backend: IsolationBackendKindSchema.optional(),
16784
+ apply: boolean2().optional(),
16785
+ merge: _enum(["patch", "branch"]).optional(),
16786
+ commits: _enum(["generic", "ai"]).optional()
16787
+ }).strict();
16761
16788
  var OmoTaskDagSettingsSchema = object({
16762
16789
  max_nodes_per_run: number2().int().positive().default(64),
16763
16790
  max_runs_per_session: number2().int().positive().default(16),
@@ -16769,6 +16796,7 @@ var OmoTaskDagSettingsSchema = object({
16769
16796
  max_prompt_bytes: number2().int().positive().default(262144)
16770
16797
  }).strict();
16771
16798
  var OmoTaskSettingsSchema = object({
16799
+ isolation: OmoTaskIsolationSchema.default(isolationDefaults),
16772
16800
  default_execution_mode: _enum(["auto", "in-process", "process"]).default("auto"),
16773
16801
  process_runner: _enum(["host", "child-process"]).default("host"),
16774
16802
  host_engine_policy: _enum(["upgrade", "fallback"]).default("upgrade"),
@@ -16817,6 +16845,7 @@ var OmoTaskWarningsLayerSchema = object({
16817
16845
  unavailable_categories: boolean2().optional()
16818
16846
  }).strict();
16819
16847
  var OmoTaskSettingsLayerSchema = object({
16848
+ isolation: OmoTaskIsolationLayerSchema.optional(),
16820
16849
  default_execution_mode: _enum(["auto", "in-process", "process"]).optional(),
16821
16850
  process_runner: _enum(["host", "child-process"]).optional(),
16822
16851
  host_engine_policy: _enum(["upgrade", "fallback"]).optional(),
@@ -16948,6 +16977,7 @@ var OmoConfigProfileSchema = object({
16948
16977
  telemetry: OmoTelemetrySettingsLayerSchema.optional(),
16949
16978
  disabled_skills: OmoDisabledSkillsSchema.optional(),
16950
16979
  "[opencode]": OmoOpenCodeHarnessConfigSchema.optional(),
16980
+ "[native]": OmoTypedHarnessConfigSchema.optional(),
16951
16981
  "[senpi]": OmoTypedHarnessConfigSchema.optional(),
16952
16982
  "[codex]": OmoTypedHarnessConfigSchema.optional()
16953
16983
  }).strict();
@@ -16966,6 +16996,7 @@ var OmoConfigSchema = object({
16966
16996
  telemetry: OmoTelemetrySettingsSchema.optional(),
16967
16997
  disabled_skills: OmoDisabledSkillsSchema.optional(),
16968
16998
  "[opencode]": OmoOpenCodeHarnessConfigSchema.optional(),
16999
+ "[native]": OmoTypedHarnessConfigSchema.optional(),
16969
17000
  "[senpi]": OmoTypedHarnessConfigSchema.optional(),
16970
17001
  "[codex]": OmoTypedHarnessConfigSchema.optional(),
16971
17002
  profiles: record(string2(), OmoConfigProfileSchema).default({}),
@@ -16987,6 +17018,7 @@ var OmoConfigLayerSchema = object({
16987
17018
  telemetry: OmoTelemetrySettingsLayerSchema.optional(),
16988
17019
  disabled_skills: OmoDisabledSkillsSchema.optional(),
16989
17020
  "[opencode]": OmoOpenCodeHarnessConfigSchema.optional(),
17021
+ "[native]": OmoTypedHarnessConfigSchema.optional(),
16990
17022
  "[senpi]": OmoTypedHarnessConfigSchema.optional(),
16991
17023
  "[codex]": OmoTypedHarnessConfigSchema.optional(),
16992
17024
  profiles: record(string2(), OmoConfigProfileSchema).optional(),
@@ -17050,6 +17082,54 @@ function canonicalizeLegacyCategoryNames(document) {
17050
17082
  return { document: canonicalized, renames };
17051
17083
  }
17052
17084
 
17085
+ // packages/omo-config-core/src/schema/legacy-harness-names.ts
17086
+ function canonicalHarnessName(name) {
17087
+ return Object.hasOwn(OMO_CONFIG_LEGACY_HARNESS_ALIASES, name) ? OMO_CONFIG_LEGACY_HARNESS_ALIASES[name] : name;
17088
+ }
17089
+ function harnessBlockKey(harness) {
17090
+ return `[${harness}]`;
17091
+ }
17092
+ function legacyHarnessOfBlockKey(key) {
17093
+ if (!key.startsWith("[") || !key.endsWith("]"))
17094
+ return;
17095
+ const harness = key.slice(1, -1);
17096
+ return Object.hasOwn(OMO_CONFIG_LEGACY_HARNESS_ALIASES, harness) ? harness : undefined;
17097
+ }
17098
+ function isRecord7(value) {
17099
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17100
+ }
17101
+ function canonicalizeBlocksIn(container, path, renames) {
17102
+ const result = {};
17103
+ for (const [key, value] of Object.entries(container)) {
17104
+ const legacyHarness = legacyHarnessOfBlockKey(key);
17105
+ if (legacyHarness === undefined) {
17106
+ result[key] = value;
17107
+ continue;
17108
+ }
17109
+ const canonical = harnessBlockKey(canonicalHarnessName(legacyHarness));
17110
+ const dropped = Object.hasOwn(container, canonical);
17111
+ renames.push({ canonical, dropped, legacy: key, path: [...path, key].join(".") });
17112
+ if (!dropped)
17113
+ result[canonical] = value;
17114
+ }
17115
+ return result;
17116
+ }
17117
+ function canonicalizeLegacyHarnessBlocks(document) {
17118
+ if (!isRecord7(document))
17119
+ return { document: {}, renames: [] };
17120
+ const renames = [];
17121
+ const canonicalized = canonicalizeBlocksIn(document, [], renames);
17122
+ const profiles = canonicalized["profiles"];
17123
+ if (isRecord7(profiles)) {
17124
+ const canonicalProfiles = {};
17125
+ for (const [name, profile] of Object.entries(profiles)) {
17126
+ canonicalProfiles[name] = isRecord7(profile) ? canonicalizeBlocksIn(profile, ["profiles", name], renames) : profile;
17127
+ }
17128
+ canonicalized["profiles"] = canonicalProfiles;
17129
+ }
17130
+ return { document: canonicalized, renames };
17131
+ }
17132
+
17053
17133
  // node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
17054
17134
  function createScanner(text, ignoreTrivia = false) {
17055
17135
  const len = text.length;
@@ -18025,7 +18105,7 @@ function resolveOmoConfigPaths(options) {
18025
18105
  }
18026
18106
 
18027
18107
  // packages/omo-config-core/src/loader/resolution.ts
18028
- var HARNESS_KEYS = [...new Set([...HARNESS_IDS, ...OMO_CONFIG_HARNESS_IDS])].map((harness) => `[${harness}]`);
18108
+ var HARNESS_KEYS = [...new Set([...HARNESS_IDS, ...OMO_CONFIG_HARNESS_IDS, ...OMO_CONFIG_LEGACY_HARNESS_IDS])].map((harness) => harnessBlockKey(harness));
18029
18109
  function profileName(value) {
18030
18110
  return value === "" ? undefined : value;
18031
18111
  }
@@ -18054,7 +18134,13 @@ function withoutControlKeys(config) {
18054
18134
  function harnessLayer(config, harness) {
18055
18135
  if (harness === undefined)
18056
18136
  return {};
18057
- return toRecord(config[`[${harness}]`]) ?? {};
18137
+ const canonical = canonicalHarnessName(harness);
18138
+ const legacyKeys = Object.entries(OMO_CONFIG_LEGACY_HARNESS_ALIASES).filter(([, target]) => target === canonical).map(([legacy]) => harnessBlockKey(legacy));
18139
+ let layer = {};
18140
+ for (const key of [...legacyKeys, harnessBlockKey(canonical)]) {
18141
+ layer = mergeOmoConfigRecords(layer, toRecord(config[key]) ?? {});
18142
+ }
18143
+ return layer;
18058
18144
  }
18059
18145
  function resolveOmoConfigView(options) {
18060
18146
  const profiles = toRecord(options.config["profiles"]);
@@ -18105,6 +18191,7 @@ var DEFAULT_RAW_CONFIG = {
18105
18191
  function stripResolutionControlKeys(config) {
18106
18192
  const {
18107
18193
  "[codex]": _codex,
18194
+ "[native]": _native,
18108
18195
  "[opencode]": _opencode,
18109
18196
  "[senpi]": _senpi,
18110
18197
  profiles: _profiles,
@@ -18130,21 +18217,21 @@ function hasUnsafeUnrecognizedKey(issues) {
18130
18217
  function hasTamperedPrototype(value) {
18131
18218
  if (Array.isArray(value))
18132
18219
  return value.some((entry) => hasTamperedPrototype(entry));
18133
- if (!isRecord7(value))
18220
+ if (!isRecord8(value))
18134
18221
  return false;
18135
18222
  const prototype = Object.getPrototypeOf(value);
18136
18223
  if (prototype !== Object.prototype && prototype !== null)
18137
18224
  return true;
18138
18225
  return Object.values(value).some((entry) => hasTamperedPrototype(entry));
18139
18226
  }
18140
- function isRecord7(value) {
18227
+ function isRecord8(value) {
18141
18228
  return typeof value === "object" && value !== null && !Array.isArray(value);
18142
18229
  }
18143
18230
  function containerAt(record, path) {
18144
18231
  let container = record;
18145
18232
  for (const segment of path) {
18146
18233
  const next = container[segment];
18147
- if (!isRecord7(next))
18234
+ if (!isRecord8(next))
18148
18235
  return null;
18149
18236
  container = next;
18150
18237
  }
@@ -18255,6 +18342,15 @@ function legacyCategoryDiagnostic(path, renames) {
18255
18342
  issuePaths: renames.map((rename) => rename.path)
18256
18343
  };
18257
18344
  }
18345
+ function legacyHarnessDiagnostic(path, renames) {
18346
+ const detail = renames.map((rename) => rename.dropped ? `${rename.path} ignored because ${rename.canonical} is also configured` : `${rename.path} renamed to ${rename.canonical}`).join(", ");
18347
+ return {
18348
+ kind: "deprecated-keys",
18349
+ message: `Deprecated harness block in ${path}: ${detail}. Rename it; the alias is removed in a future release.`,
18350
+ path,
18351
+ issuePaths: renames.map((rename) => rename.path)
18352
+ };
18353
+ }
18258
18354
  function loadOmoConfig(options = {}) {
18259
18355
  const fileSystem = options.fileSystem ?? DEFAULT_READ_FILE_SYSTEM;
18260
18356
  const cwd = options.cwd ?? process.cwd();
@@ -18277,8 +18373,12 @@ function loadOmoConfig(options = {}) {
18277
18373
  if (canonicalized.renames.length > 0) {
18278
18374
  diagnostics.push(legacyCategoryDiagnostic(candidate.path, canonicalized.renames));
18279
18375
  }
18280
- layers.push({ config: canonicalized.document, source: loaded.source });
18281
- merged = mergeOmoConfigRecords(merged, canonicalized.document);
18376
+ const harnessCanonicalized = canonicalizeLegacyHarnessBlocks(canonicalized.document);
18377
+ if (harnessCanonicalized.renames.length > 0) {
18378
+ diagnostics.push(legacyHarnessDiagnostic(candidate.path, harnessCanonicalized.renames));
18379
+ }
18380
+ layers.push({ config: harnessCanonicalized.document, source: loaded.source });
18381
+ merged = mergeOmoConfigRecords(merged, harnessCanonicalized.document);
18282
18382
  }
18283
18383
  }
18284
18384
  const requestedProfile = resolveOmoProfileName({
@@ -20878,10 +20978,10 @@ function readCatalogMultiAgentVersion(model, cachePath) {
20878
20978
  } catch {
20879
20979
  return null;
20880
20980
  }
20881
- if (!isRecord8(cache) || !Array.isArray(cache.models))
20981
+ if (!isRecord9(cache) || !Array.isArray(cache.models))
20882
20982
  return null;
20883
20983
  for (const entry of cache.models) {
20884
- if (!isRecord8(entry))
20984
+ if (!isRecord9(entry))
20885
20985
  continue;
20886
20986
  if (entry.slug !== model && entry.id !== model)
20887
20987
  continue;
@@ -20906,7 +21006,7 @@ function readRootModelCatalogPath(config) {
20906
21006
  const single = config.match(/^\s*model_catalog_json\s*=\s*'([^']+)'/m);
20907
21007
  return single?.[1] ?? null;
20908
21008
  }
20909
- function isRecord8(value) {
21009
+ function isRecord9(value) {
20910
21010
  return typeof value === "object" && value !== null && !Array.isArray(value);
20911
21011
  }
20912
21012
  function removeFeatureFlagSetting(config, featureName) {
@@ -0,0 +1,25 @@
1
+ # ATTRIBUTION / NOTICE
2
+
3
+ This skill (`browser`, part of `@oh-my-opencode/shared-skills`) is project-original documentation.
4
+ It drives a third-party tool that it does **not** redistribute.
5
+
6
+ ## BrowserSkill — driven, not vendored
7
+
8
+ The attached engine this skill documents is **BrowserSkill** by Tencent.
9
+
10
+ - Upstream: https://github.com/Tencent/BrowserSkill
11
+ - License: MIT
12
+ - What ships here: documentation and two local helper scripts, authored by this project.
13
+ - What does NOT ship here: the `bsk` CLI, the daemon, the browser extension, or any upstream
14
+ source. The user installs those from upstream's own installer and from the Chrome Web Store /
15
+ Edge Add-ons listings. This package never bundles a browser binary or an extension payload.
16
+
17
+ MIT permits redistribution; we simply have no reason to, because upstream publishes signed
18
+ per-platform releases and store builds that stay current on their own.
19
+
20
+ ## Owned-engine references
21
+
22
+ `references/owned-engine/` documents a *contract* for a code-driven browser — the escalation
23
+ ladder, viewport pinning, reading the network instead of the DOM, overlay and cross-origin
24
+ handling — not any particular package. The techniques are generic CDP practice. No third-party
25
+ source is reproduced.