omo-ai 5.1.34 → 5.1.38
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/bin/lib/engine-environment.js +113 -0
- package/bin/lib/herdr-agent-hint.js +29 -0
- package/bin/lib/launcher.js +7 -102
- package/bin/lib/provider-map.json +1 -1
- package/bin/lib/setup-assets-import.js +2 -1
- package/bin/lib/setup-model-choices-import.js +5 -1
- package/package.json +2 -2
- package/plugin/CHANGELOG.md +140 -0
- package/plugin/extensions/gateway-rules-extension.mjs +1 -1
- package/plugin/extensions/gateway-store-worker.mjs +2 -2
- package/plugin/extensions/memory-run-supervisor.mjs +2 -2
- package/plugin/extensions/omo-computer-use.js +2 -2
- package/plugin/extensions/omo-init-deep-advisor.js +2 -2
- package/plugin/extensions/omo-member.js +2 -2
- package/plugin/extensions/omo-memory-doctor.js +2 -2
- package/plugin/extensions/omo-memory-memfs.js +2 -2
- package/plugin/extensions/omo-memory-palace.js +2 -0
- package/plugin/extensions/omo-side-panel.js +2 -2
- package/plugin/extensions/omo-task.js +2 -2
- package/plugin/extensions/omo-thread-client.js +2 -2
- package/plugin/extensions/omo.js +2 -2
- package/plugin/package.json +2 -1
- package/plugin/runtime/agent-toolkit-sdk/sdk.js +2 -2
- package/plugin/runtime/ast-grep-mcp/manifest.json +1 -1
- package/plugin/runtime/category-coverage/index.js +2 -2
- package/plugin/runtime/lsp-daemon/dist/.omo-runtime-manifest.json +1 -1
- package/plugin/runtime/rollback-migrate.js +2 -2
- package/plugin/runtime/thread-sdk/sdk.js +2 -2
- package/plugin/scripts/install.mjs +1 -0
- package/plugin/skills/browser/runtime/omowright/index.js +4 -4
- package/plugin/skills/browser/runtime/omowright/manifest.json +2 -2
- package/plugin/skills/frontend/SKILL.md +8 -1
- package/plugin/skills/frontend/references/design/README.md +3 -2
- package/plugin/skills/frontend/references/perfection/README.md +2 -1
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/color.ts +132 -0
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/css-parse.ts +103 -0
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/declarations.ts +156 -0
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/findings.ts +34 -0
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/gradient.ts +70 -0
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/lint.ts +110 -0
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/markup.ts +148 -0
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/selector.ts +46 -0
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/tailwind.ts +117 -0
- package/plugin/skills/frontend/scripts/perfection/check-accent-stripes.ts +50 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/exempt-states.css +4 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/flat-stripe-side.tsx +11 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/full-surface-gradients.css +34 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/inline-ok.tsx +7 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/neutral-and-flat.css +6 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/pseudo-overlays.css +3 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/tailwind-ok.tsx +11 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/uniform-and-rings.css +3 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/wide-blocks-and-hairlines.css +3 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/apply.css +3 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/border-top-card.css +5 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/cva-variant.ts +1 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/gradient-edges.css +26 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/inline-styles.tsx +6 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/inset-shadow.css +4 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/non-visible-focus-and-nested.tsx +6 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/pseudo-bar.css +13 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/split-rule-and-token.scss +14 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/styled-components.tsx +14 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/tailwind-cards.tsx +8 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/tailwind-inset-and-style.vue +11 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/tailwind-pseudo.html +3 -0
- package/plugin/skills/ulw-research/references/report-gates.md +1 -1
- package/plugin/skills/ulw-research/scripts/gates-figures.mjs +22 -6
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync, realpathSync } from "node:fs"
|
|
2
|
+
import { delimiter, isAbsolute, join, relative, sep } from "node:path"
|
|
3
|
+
import { canonicalAgentDir } from "./agent-dir.js"
|
|
4
|
+
import { HERDR_AGENT_MARKER } from "./herdr-agent-hint.js"
|
|
5
|
+
import { nearestNodeBin, packageManifest, packageRoot, readJson, releaseChannel, updateTarget } from "./package-paths.js"
|
|
6
|
+
|
|
7
|
+
// Identity the engine adopts for this install: what the user sees, where state lives, which
|
|
8
|
+
// environment prefix is read first, what goes on the wire, and which channel to check for
|
|
9
|
+
// updates. The engine consumes this once and scrubs it, so nested engine processes are
|
|
10
|
+
// unaffected.
|
|
11
|
+
function pluginChangelogSource() {
|
|
12
|
+
try {
|
|
13
|
+
const pluginRoot = join(packageRoot, "plugin")
|
|
14
|
+
const changelogPath = join(pluginRoot, "CHANGELOG.md")
|
|
15
|
+
if (!existsSync(changelogPath)) return undefined
|
|
16
|
+
const version = readJson(join(pluginRoot, "package.json")).version
|
|
17
|
+
return typeof version === "string" && version ? { path: changelogPath, version } : { path: changelogPath }
|
|
18
|
+
} catch {
|
|
19
|
+
return undefined
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function brandProfile() {
|
|
24
|
+
const update = updateTarget()
|
|
25
|
+
// The changelog source is advisory: a missing plugin manifest or file must disable
|
|
26
|
+
// startup notes, never fail the launch.
|
|
27
|
+
const changelog = pluginChangelogSource()
|
|
28
|
+
return {
|
|
29
|
+
name: "OmO",
|
|
30
|
+
command: "omo",
|
|
31
|
+
displayVersion: packageManifest().version,
|
|
32
|
+
configDir: ".omo",
|
|
33
|
+
// Engine state lives at the canonical `~/.omo/agent`, never directly under the config
|
|
34
|
+
// directory: a flat home would disagree with the directory every omo surface resolves.
|
|
35
|
+
flatLayout: false,
|
|
36
|
+
envPrefix: "OMO",
|
|
37
|
+
userAgent: "omo",
|
|
38
|
+
originator: "omo",
|
|
39
|
+
...(changelog ? { changelog } : {}),
|
|
40
|
+
update: {
|
|
41
|
+
packageName: "omo-ai",
|
|
42
|
+
distTag: releaseChannel(),
|
|
43
|
+
command: update.command,
|
|
44
|
+
changelogUrl: "https://github.com/code-yeongyu/oh-my-openagent/releases",
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function canonicalPath(path) {
|
|
50
|
+
try {
|
|
51
|
+
return realpathSync.native(path)
|
|
52
|
+
} catch {
|
|
53
|
+
return path
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function containsPath(root, target) {
|
|
58
|
+
const rel = relative(canonicalPath(root), canonicalPath(target))
|
|
59
|
+
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// A compiled omo session exports its own payload as the brand-scoped package dir, and every shell it
|
|
63
|
+
// spawns inherits that. The engine reads these names before the legacy PI_PACKAGE_DIR, so a release
|
|
64
|
+
// omo started from such a shell would run on the foreign payload. A deliberate relocation names this
|
|
65
|
+
// install's engine and survives; a root that does not contain the engine belongs to another install.
|
|
66
|
+
function dropForeignPackageDirs(env, senpiRoot) {
|
|
67
|
+
for (const name of ["OMO_PACKAGE_DIR", "SENPI_PACKAGE_DIR"]) {
|
|
68
|
+
const root = env[name]
|
|
69
|
+
if (root && !containsPath(root, senpiRoot)) delete env[name]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The one environment every engine this launcher starts gets. `overrides` (a host call's own
|
|
75
|
+
* environment) is merged in here, before the last step, so nothing a caller passes can put back
|
|
76
|
+
* what this function removes.
|
|
77
|
+
*/
|
|
78
|
+
export function senpiEnvironment(senpiRoot, overrides = {}) {
|
|
79
|
+
const env = { ...process.env }
|
|
80
|
+
delete env.OMO_BIN
|
|
81
|
+
delete env.SENPI_BIN
|
|
82
|
+
dropForeignPackageDirs(env, senpiRoot)
|
|
83
|
+
// One directory for every surface. The legacy name travels too, so a bare senpi spawned by a
|
|
84
|
+
// tool inherits the same state instead of falling back to its own home.
|
|
85
|
+
const agentDir = canonicalAgentDir(env)
|
|
86
|
+
env.OMO_CODING_AGENT_DIR = agentDir
|
|
87
|
+
env.SENPI_CODING_AGENT_DIR = agentDir
|
|
88
|
+
// senpi's footer reads this marker to show the OmO Native badge for omo-ai installs, which load
|
|
89
|
+
// the plugin via --extension and therefore never match the settings-packages detection gates.
|
|
90
|
+
env.OMO_NATIVE = "1"
|
|
91
|
+
// This launcher already decided which runtime the product runs on, possibly by re-execing itself
|
|
92
|
+
// under bun. Handing that answer down stops the engine from making its own, conflicting choice.
|
|
93
|
+
env.SENPI_RUNTIME = process.versions.bun ? "bun" : "node"
|
|
94
|
+
env.SENPI_BRAND = JSON.stringify(brandProfile())
|
|
95
|
+
const binDir = nearestNodeBin(senpiRoot)
|
|
96
|
+
if (binDir) {
|
|
97
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH"
|
|
98
|
+
const path = env[pathKey]
|
|
99
|
+
env[pathKey] = path ? `${binDir}${delimiter}${path}` : binDir
|
|
100
|
+
const shim = join(binDir, process.platform === "win32" ? "senpi.cmd" : "senpi")
|
|
101
|
+
if (existsSync(shim)) env.SENPI_BIN = shim
|
|
102
|
+
}
|
|
103
|
+
// Anything resolving the product by name must re-enter through this launcher, otherwise it
|
|
104
|
+
// would reach the engine directly and lose the brand.
|
|
105
|
+
env.OMO_BIN = join(packageRoot, "bin", "omo.js")
|
|
106
|
+
const merged = { ...env, ...overrides }
|
|
107
|
+
// Only the interactive launch writes this record of the herdr hint it set (herdr-agent-hint.js),
|
|
108
|
+
// after this function returns. No engine started here may carry one from the parent environment
|
|
109
|
+
// or from a caller's overrides: the plugin would treat it as the launcher's and could remove a
|
|
110
|
+
// HERDR_AGENT the user set (#10448).
|
|
111
|
+
delete merged[HERDR_AGENT_MARKER]
|
|
112
|
+
return merged
|
|
113
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// herdr names the agent that owns a pane from the pane's foreground process (#10448). The engine
|
|
2
|
+
// runs as `bun …/cli.js` (or `node …/cli.js`), which herdr cannot place, so `herdr agent prompt`
|
|
3
|
+
// refused every OmO pane. HERDR_AGENT is the hint herdr reads from that process's environment; OmO
|
|
4
|
+
// is a pi-family agent to herdr, which already lists these panes as `pi`.
|
|
5
|
+
//
|
|
6
|
+
// The hint is only for an interactive engine the user sees in the pane: a TTY on stdin and no
|
|
7
|
+
// print or protocol flag before `--`. A HERDR_AGENT the user set is left alone. The launcher also
|
|
8
|
+
// sets OMO_HERDR_AGENT_HINT to the value it wrote, so the plugin releases only a hint the launcher
|
|
9
|
+
// set (packages/omo-senpi/src/extension/herdr-agent-hint.ts). Only this function writes that
|
|
10
|
+
// record: one inherited from the parent environment is dropped first, so it can never vouch for a
|
|
11
|
+
// HERDR_AGENT the user set.
|
|
12
|
+
|
|
13
|
+
const NON_INTERACTIVE_FLAGS = new Set(["-p", "--print", "--mode"])
|
|
14
|
+
|
|
15
|
+
export const HERDR_AGENT_LABEL = "pi"
|
|
16
|
+
export const HERDR_AGENT_MARKER = "OMO_HERDR_AGENT_HINT"
|
|
17
|
+
|
|
18
|
+
export function withHerdrAgentHint(env, args, stdinIsTTY = process.stdin.isTTY === true) {
|
|
19
|
+
delete env[HERDR_AGENT_MARKER]
|
|
20
|
+
if (env.HERDR_ENV !== "1") return env
|
|
21
|
+
if (env.HERDR_AGENT !== undefined && env.HERDR_AGENT !== "") return env
|
|
22
|
+
if (!stdinIsTTY) return env
|
|
23
|
+
const end = args.indexOf("--")
|
|
24
|
+
const flags = end === -1 ? args : args.slice(0, end)
|
|
25
|
+
if (flags.some((arg) => NON_INTERACTIVE_FLAGS.has(arg) || arg.startsWith("--mode="))) return env
|
|
26
|
+
env.HERDR_AGENT = HERDR_AGENT_LABEL
|
|
27
|
+
env[HERDR_AGENT_MARKER] = HERDR_AGENT_LABEL
|
|
28
|
+
return env
|
|
29
|
+
}
|
package/bin/lib/launcher.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process"
|
|
2
|
-
import { existsSync
|
|
3
|
-
import {
|
|
2
|
+
import { existsSync } from "node:fs"
|
|
3
|
+
import { join } from "node:path"
|
|
4
4
|
import { isEngineCommand } from "./engine-commands.js"
|
|
5
5
|
import { spawnNode } from "./child-process.js"
|
|
6
6
|
import { doctorCoverageLines } from "./category-coverage.js"
|
|
@@ -13,7 +13,9 @@ import { runDoctor } from "./doctor.js"
|
|
|
13
13
|
import { ensureEnginePrepared, preparePluginLaunchSpec } from "./engine-prepare.js"
|
|
14
14
|
import { migrateLegacyBunGlobalManifest } from "./legacy-bun-global-migration.js"
|
|
15
15
|
import { adoptLegacyFlatState, canonicalAgentDir } from "./agent-dir.js"
|
|
16
|
-
import {
|
|
16
|
+
import { senpiEnvironment } from "./engine-environment.js"
|
|
17
|
+
import { withHerdrAgentHint } from "./herdr-agent-hint.js"
|
|
18
|
+
import { packageManifest, packageRoot, readJson, releaseBanner, resolveSenpi, updateTarget } from "./package-paths.js"
|
|
17
19
|
import { runSelfUpdate } from "./self-update.js"
|
|
18
20
|
import { isSelfUpdate } from "./update-args.js"
|
|
19
21
|
import { detectHarnesses } from "./setup-detect.js"
|
|
@@ -21,48 +23,6 @@ import { readSetupSuggestionCache, spawnSetupSuggestionRefresh } from "./setup-d
|
|
|
21
23
|
import { printSetupReport } from "./setup-report.js"
|
|
22
24
|
import { refuseRetiredCommand } from "./retired-command.js"
|
|
23
25
|
|
|
24
|
-
// Identity the engine adopts for this install: what the user sees, where state lives, which
|
|
25
|
-
// environment prefix is read first, what goes on the wire, and which channel to check for
|
|
26
|
-
// updates. The engine consumes this once and scrubs it, so nested engine processes are
|
|
27
|
-
// unaffected.
|
|
28
|
-
function pluginChangelogSource() {
|
|
29
|
-
try {
|
|
30
|
-
const pluginRoot = join(packageRoot, "plugin")
|
|
31
|
-
const changelogPath = join(pluginRoot, "CHANGELOG.md")
|
|
32
|
-
if (!existsSync(changelogPath)) return undefined
|
|
33
|
-
const version = readJson(join(pluginRoot, "package.json")).version
|
|
34
|
-
return typeof version === "string" && version ? { path: changelogPath, version } : { path: changelogPath }
|
|
35
|
-
} catch {
|
|
36
|
-
return undefined
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function brandProfile() {
|
|
41
|
-
const update = updateTarget()
|
|
42
|
-
// The changelog source is advisory: a missing plugin manifest or file must disable
|
|
43
|
-
// startup notes, never fail the launch.
|
|
44
|
-
const changelog = pluginChangelogSource()
|
|
45
|
-
return {
|
|
46
|
-
name: "OmO",
|
|
47
|
-
command: "omo",
|
|
48
|
-
displayVersion: packageManifest().version,
|
|
49
|
-
configDir: ".omo",
|
|
50
|
-
// Engine state lives at the canonical `~/.omo/agent`, never directly under the config
|
|
51
|
-
// directory: a flat home would disagree with the directory every omo surface resolves.
|
|
52
|
-
flatLayout: false,
|
|
53
|
-
envPrefix: "OMO",
|
|
54
|
-
userAgent: "omo",
|
|
55
|
-
originator: "omo",
|
|
56
|
-
...(changelog ? { changelog } : {}),
|
|
57
|
-
update: {
|
|
58
|
-
packageName: "omo-ai",
|
|
59
|
-
distTag: releaseChannel(),
|
|
60
|
-
command: update.command,
|
|
61
|
-
changelogUrl: "https://github.com/code-yeongyu/oh-my-openagent/releases",
|
|
62
|
-
},
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
26
|
function engineVersion() {
|
|
67
27
|
try {
|
|
68
28
|
return readJson(join(resolveSenpi().packageRoot, "package.json")).version
|
|
@@ -71,62 +31,6 @@ function engineVersion() {
|
|
|
71
31
|
}
|
|
72
32
|
}
|
|
73
33
|
|
|
74
|
-
function canonicalPath(path) {
|
|
75
|
-
try {
|
|
76
|
-
return realpathSync.native(path)
|
|
77
|
-
} catch {
|
|
78
|
-
return path
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function containsPath(root, target) {
|
|
83
|
-
const rel = relative(canonicalPath(root), canonicalPath(target))
|
|
84
|
-
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// A compiled omo session exports its own payload as the brand-scoped package dir, and every shell it
|
|
88
|
-
// spawns inherits that. The engine reads these names before the legacy PI_PACKAGE_DIR, so a release
|
|
89
|
-
// omo started from such a shell would run on the foreign payload. A deliberate relocation names this
|
|
90
|
-
// install's engine and survives; a root that does not contain the engine belongs to another install.
|
|
91
|
-
function dropForeignPackageDirs(env, senpiRoot) {
|
|
92
|
-
for (const name of ["OMO_PACKAGE_DIR", "SENPI_PACKAGE_DIR"]) {
|
|
93
|
-
const root = env[name]
|
|
94
|
-
if (root && !containsPath(root, senpiRoot)) delete env[name]
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function senpiEnvironment(senpiRoot) {
|
|
99
|
-
const env = { ...process.env }
|
|
100
|
-
delete env.OMO_BIN
|
|
101
|
-
delete env.SENPI_BIN
|
|
102
|
-
dropForeignPackageDirs(env, senpiRoot)
|
|
103
|
-
// One directory for every surface. The legacy name travels too, so a bare senpi spawned by a
|
|
104
|
-
// tool inherits the same state instead of falling back to its own home.
|
|
105
|
-
const agentDir = canonicalAgentDir(env)
|
|
106
|
-
env.OMO_CODING_AGENT_DIR = agentDir
|
|
107
|
-
env.SENPI_CODING_AGENT_DIR = agentDir
|
|
108
|
-
// senpi's footer reads this marker to show the OmO Native badge for omo-ai installs, which load
|
|
109
|
-
// the plugin via --extension and therefore never match the settings-packages detection gates.
|
|
110
|
-
env.OMO_NATIVE = "1"
|
|
111
|
-
// This launcher already decided which runtime the product runs on, possibly by re-execing itself
|
|
112
|
-
// under bun. Handing that answer down stops the engine from making its own, conflicting choice.
|
|
113
|
-
env.SENPI_RUNTIME = process.versions.bun ? "bun" : "node"
|
|
114
|
-
env.SENPI_BRAND = JSON.stringify(brandProfile())
|
|
115
|
-
|
|
116
|
-
const binDir = nearestNodeBin(senpiRoot)
|
|
117
|
-
if (binDir) {
|
|
118
|
-
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH"
|
|
119
|
-
const path = env[pathKey]
|
|
120
|
-
env[pathKey] = path ? `${binDir}${delimiter}${path}` : binDir
|
|
121
|
-
const shim = join(binDir, process.platform === "win32" ? "senpi.cmd" : "senpi")
|
|
122
|
-
if (existsSync(shim)) env.SENPI_BIN = shim
|
|
123
|
-
}
|
|
124
|
-
// Anything resolving the product by name must re-enter through this launcher, otherwise it
|
|
125
|
-
// would reach the engine directly and lose the brand.
|
|
126
|
-
env.OMO_BIN = join(packageRoot, "bin", "omo.js")
|
|
127
|
-
return env
|
|
128
|
-
}
|
|
129
|
-
|
|
130
34
|
function preparedSenpi() {
|
|
131
35
|
preparePluginLaunchSpec({ pluginRoot: join(packageRoot, "plugin") })
|
|
132
36
|
const senpi = resolveSenpi()
|
|
@@ -144,6 +48,7 @@ async function spawnSenpi(args, withExtension) {
|
|
|
144
48
|
? ["--extension", join(packageRoot, "plugin"), ...args]
|
|
145
49
|
: args
|
|
146
50
|
const env = senpiEnvironment(senpi.packageRoot)
|
|
51
|
+
if (withExtension) withHerdrAgentHint(env, args)
|
|
147
52
|
if (process.platform !== "win32" && typeof process.execve === "function") {
|
|
148
53
|
try {
|
|
149
54
|
process.execve(process.execPath, [process.execPath, senpi.cliPath, ...finalArgs], env)
|
|
@@ -203,7 +108,7 @@ export function engineHostCall(engineArgs, options) {
|
|
|
203
108
|
const senpi = preparedSenpi()
|
|
204
109
|
const result = spawnSync(process.execPath, [senpi.cliPath, ...engineArgs], {
|
|
205
110
|
encoding: "utf8",
|
|
206
|
-
env:
|
|
111
|
+
env: senpiEnvironment(senpi.packageRoot, options.env),
|
|
207
112
|
windowsHide: true,
|
|
208
113
|
})
|
|
209
114
|
return { exitCode: result.status ?? 1, stdout: result.stdout ?? "", stderr: result.stderr ?? "" }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "Derived from @code-yeongyu/senpi 2026.10.
|
|
2
|
+
"_comment": "Derived from @code-yeongyu/senpi 2026.10.19 builtinProviders(). builtinProviderIds and oauthProviderIds are the engine registry verbatim; providers maps a source harness id onto the omo provider that serves the same endpoint; oauthLogins maps a source OAuth id onto the omo provider to run /login for. Re-derive all four whenever the senpi pin changes.",
|
|
3
3
|
"builtinProviderIds": [
|
|
4
4
|
"alibaba-token-plan",
|
|
5
5
|
"amazon-bedrock",
|
|
@@ -119,7 +119,8 @@ export function planAssets({ runtime, agentDir }) {
|
|
|
119
119
|
const notices = [...source.notices]
|
|
120
120
|
const present = source.mcpServers.length > 0 || source.skills.length > 0
|
|
121
121
|
const paths = { mcp: join(agentDir, "mcp.json"), skills: join(agentDir, "skills") }
|
|
122
|
-
const
|
|
122
|
+
const pluginRoot = runtime.pluginRoot ?? join(packageRoot, "plugin")
|
|
123
|
+
const result = classifyAssets(source, paths, bundledSkillNames(join(pluginRoot, "skills")))
|
|
123
124
|
if (result.servers.blocked.length > 0) notices.push(`WARN senpi: malformed mcp.json; these servers were not imported: ${result.servers.blocked.join(", ")}`)
|
|
124
125
|
return {
|
|
125
126
|
agentDir,
|
|
@@ -177,7 +177,11 @@ export async function planModelChoices(stage) {
|
|
|
177
177
|
if (!hasModelChoices(raw)) return empty
|
|
178
178
|
let registry
|
|
179
179
|
try {
|
|
180
|
-
registry = await (stage.loadRegistry ?? loadEngineModels)({
|
|
180
|
+
registry = await (stage.loadRegistry ?? loadEngineModels)({
|
|
181
|
+
agentDir: stage.agentDir,
|
|
182
|
+
customProviders: stage.customProviders,
|
|
183
|
+
loadCatalog: stage.runtime.loadModelCatalog,
|
|
184
|
+
})
|
|
181
185
|
} catch (error) {
|
|
182
186
|
return { ...empty, notices: [...raw.notices, `WARN senpi: could not read the engine's model list (${error.message}); model choices were not carried`] }
|
|
183
187
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omo-ai",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.38",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OmO Native - the standalone omo command with the OMO extension built in: bun add -g omo-ai",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"postinstall": "node bin/senpi-patch.mjs"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@code-yeongyu/senpi": "2026.10.
|
|
20
|
+
"@code-yeongyu/senpi": "2026.10.19"
|
|
21
21
|
},
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
package/plugin/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,146 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [5.1.38] - 2026-10-19
|
|
11
|
+
|
|
12
|
+
5.1.37 was tagged but its publish stopped on Linux (the release binaries outgrew their size budget), so its main packages never reached npm; everything listed for it ships in this release. ([#10566](https://github.com/code-yeongyu/oh-my-openagent/pull/10566))
|
|
13
|
+
|
|
14
|
+
**Release binaries may be up to 182 MiB.** The per-platform size budget moves from 160 MiB to 182 MiB after measuring what grew: the Bun 1.4.3 runtime adds 9.5 MB to every binary and the senpi update about 1.25 MB, so linux-x64 reached 173.4 MB. Nothing was bundled by mistake. ([#10566](https://github.com/code-yeongyu/oh-my-openagent/pull/10566))
|
|
15
|
+
|
|
16
|
+
5.1.36 was prepared but never published; everything listed for it ships in this release. ([#10552](https://github.com/code-yeongyu/oh-my-openagent/pull/10552))
|
|
17
|
+
|
|
18
|
+
**The engine moves to senpi 2026.10.19: a hung extension handler no longer freezes a session, and queued input survives a crash.** An extension handler that never settles on a lifecycle event is named in the session after 60 s and released after 20 min, or at once on Esc or the turn deadline. Input queued with a client id is written to the session file before it is queued and delivered exactly once. The mouse wheel scrolls the transcript again while a question prompt is waiting, Alt+Q restores queued messages on WSL, and the /btw panel no longer repeats a running eval's output in the scrollback. Anthropic requests of 4096+ characters to `api.anthropic.com` are now sent gzip-compressed, as Claude Code does. ([senpi#3650](https://github.com/code-yeongyu/senpi/issues/3650), [senpi#3549](https://github.com/code-yeongyu/senpi/issues/3549), [senpi#3604](https://github.com/code-yeongyu/senpi/issues/3604), [senpi#3628](https://github.com/code-yeongyu/senpi/issues/3628), [senpi#3649](https://github.com/code-yeongyu/senpi/issues/3649), [senpi#3643](https://github.com/code-yeongyu/senpi/issues/3643), [#10552](https://github.com/code-yeongyu/oh-my-openagent/pull/10552))
|
|
19
|
+
|
|
20
|
+
**Goals keep going while they make progress, and long sessions open and stay light (senpi 2026.10.18-1).** A goal no longer stops after a fixed 150 automatic continuations; it stops only after 60 in a row with no new tool effect, todo change or message from a person, and says why and that any message resumes it. Opening a very large session on a shared host no longer stalls the other sessions, a reopened session keeps only its latest compaction window in memory (four of the largest local transcripts went from 1441 MB to 216 MB of live heap), and reading a very long history over RPC pages instead of dropping the connection. A Claude subscription from `CLAUDE_CODE_OAUTH_TOKEN` or an ambient Claude Code login now shows as a connected subscription, and a host daemon inherits every credential variable the engine reads. Provider accounts can be ordered, and grep starts 5 to 14 `rg` processes where it used to start 63 to 87. ([#10280](https://github.com/code-yeongyu/oh-my-openagent/issues/10280), [#10509](https://github.com/code-yeongyu/oh-my-openagent/pull/10509)) The engine also fixes ChatGPT browser sign-in dead-ending on "State mismatch." when another session holds the callback port ([senpi #3627](https://github.com/code-yeongyu/senpi/pull/3627)), and a compiled engine now finds the Claude Code binary shipped beside its executable instead of failing the lookup ([senpi #3640](https://github.com/code-yeongyu/senpi/pull/3640)).
|
|
21
|
+
|
|
22
|
+
**A client re-attaching to a retained session gets the full task snapshot again, and can pull it on demand.** The task RPC bridge listens for `session_resumed`, which senpi fires when the first client re-attaches to a still-open session, and re-emits the complete `omo.task.updated` snapshot instead of waiting for the next store change to alter its fingerprint; a new `omo.task.list` request answers the same payload for the parent session it names. ([#10016](https://github.com/code-yeongyu/oh-my-openagent/issues/10016), [#10122](https://github.com/code-yeongyu/oh-my-openagent/pull/10122))
|
|
23
|
+
**Memory recall's search index takes a quarter of the memory it did, and a memory write no longer rebuilds it.** Every session that recalls memory keeps a search index of the whole memory. It stored each note's words as a separate table, so a memory of 6,406 notes cost about 231 MB per session, and each memory write threw the index away and rebuilt it, taking 3 to 11 seconds. Words are now numbered once and each note keeps a compact list of numbers that survives a memory write, so the same memory costs 55 MB and a write updates the index in about 16 ms. The notes found and their order are unchanged. ([#10510](https://github.com/code-yeongyu/oh-my-openagent/issues/10510), [#10512](https://github.com/code-yeongyu/oh-my-openagent/pull/10512))
|
|
24
|
+
|
|
25
|
+
**A background task child follows its session when another client replaces it.** A raw RPC client that issues `new_session`, `switch_session` or `fork` on a daemon child's routing handle moves that session to a new durable id and transcript; the child now observes the `session_replaced` record, re-stamps its record's session path and durable id, appends a `replaced` task event, and reattaches to the new transcript instead of the abandoned one. When the replacement stops a turn the child was running, the child ends `interrupted` with the reason and a `task_send` action, and the parent is notified because it did not ask for that stop (a parent's own interrupt stays silent); this needs a senpi that names the cause on `agent_end` (senpi#3357), and an older host keeps the previous error. ([#10018](https://github.com/code-yeongyu/oh-my-openagent/issues/10018), [#10112](https://github.com/code-yeongyu/oh-my-openagent/pull/10112))
|
|
26
|
+
**`unspecified-low` tries GLM 5.3 before GPT.** A user with Claude and Z.ai (or OpenCode Go) but no Xiaomi or xAI provider used to fall from Sonnet 5.5 straight to GPT-6.1 Sol once Sonnet was unavailable. The chain now has `glm-5.3` at high thinking right after Grok 4.7 and ahead of the GPT rungs, so the order for users who hold MiMo or Grok is unchanged. ([#10306](https://github.com/code-yeongyu/oh-my-openagent/issues/10306), [#10311](https://github.com/code-yeongyu/oh-my-openagent/pull/10311))
|
|
27
|
+
|
|
28
|
+
**`omo --help` no longer moves your OpenCode config before you start a session.** On a fresh install `--help` (and `--list-models` / `--list-tips`) loads the omo extension, and its startup migration moved `~/.config/opencode/oh-my-openagent.json` into `~/.omo/migration-backup-*`, so a 4.19.x OpenCode plugin fell back to its defaults. The migration now runs only on launches that can start a session, and `--help` still lists omo's own flags. Thanks to @bluet for the report and @twotnguyen for #10433. ([#10428](https://github.com/code-yeongyu/oh-my-openagent/issues/10428), [#10454](https://github.com/code-yeongyu/oh-my-openagent/pull/10454))
|
|
29
|
+
|
|
30
|
+
**Built, tested and released on Bun 1.4.3.** CI, the release workflows and the devcontainer move from Bun 1.4.2 to 1.4.3, the current stable release, which fixes CVE-2026-48618 (TLS host names) and idle garbage collection in workers. ([#10432](https://github.com/code-yeongyu/oh-my-openagent/pull/10432))
|
|
31
|
+
|
|
32
|
+
**OpenCode edition: Sisyphus names GPT-6.1 Sol and GPT-6 Luna as themselves.** On `gpt-6.1-sol` (and its `-fast`, dashed and Venice `gpt-61-sol` ids) and on `gpt-6-luna`, the Sisyphus and Sisyphus-Junior GPT prompts introduced the model as "a GPT-family model"; they now say "GPT-6.1 Sol" and "GPT-6 Luna". ([#10291](https://github.com/code-yeongyu/oh-my-openagent/issues/10291), [#10292](https://github.com/code-yeongyu/oh-my-openagent/pull/10292))
|
|
33
|
+
|
|
34
|
+
**Memory session shutdown waits for the work it started in the memory directory.** Shutdown now waits, within its existing drain budget, for an in-flight repo maintenance pass and for the status refreshes started at bind and settle, all of which run git inside the identity directory. Before, they could still hold files open after shutdown returned; on Windows the next delete of that directory failed with `EBUSY`/`EFAULT`, which is how the memory pressure-dream test failed intermittently in CI. ([#10154](https://github.com/code-yeongyu/oh-my-openagent/issues/10154), [#10312](https://github.com/code-yeongyu/oh-my-openagent/pull/10312))
|
|
35
|
+
|
|
36
|
+
**Oh My OpenAgent loads on OpenCode 2.** OpenCode 2 refused the plugin with `Plugin must export a default definition with an id and an effect or setup function`, because both entry points exported only the OpenCode 1.x shape. The server and TUI entries now also export an OpenCode 2 `setup` built with `Plugin.define` from `@opencode/plugin`, next to the OpenCode 1.x `server` and `tui` entries, which keep working as before. Each OpenCode 2 start shows a notice that points to OmO Native, with the install command and guide link. ([#8485](https://github.com/code-yeongyu/oh-my-openagent/issues/8485), [#10439](https://github.com/code-yeongyu/oh-my-openagent/pull/10439))
|
|
37
|
+
|
|
38
|
+
**The frontend skill keeps coloured edge stripes off rounded cards, and a linter catches the ones that slip through.** A card with a coloured stripe along one edge, its rounded corners bending the stripe, is one of the most recognizable marks of generated UI, and the skill only banned it when the stripe marked a selected or focused item. It now rules the stripe out before drafting, whether it decorates the card or marks state, and offers what to use instead: a tonal surface, a heading in the accent colour, a glyph, or spacing. The new `check-accent-stripes.ts` script finds the pattern in CSS, Tailwind classes, inline styles, and styled-components, and the skill's checklist runs it before visual QA. Focus rings, form validation, neutral dividers, and tables are left alone. ([#10437](https://github.com/code-yeongyu/oh-my-openagent/issues/10437))
|
|
39
|
+
|
|
40
|
+
**A store extension's `orderBy` can only name columns its statement returns, and its text never reaches the SQL.** `orderBy` used to be pasted into the gateway's ordered read as given, so a `LIMIT`, an `OFFSET` or a column the statement does not select failed as broken SQL (`near "LIMIT": syntax error`, `no such column`). Each comma-separated term must now be one of the statement's own output columns (matched case-insensitively, as SQLite does), optionally with `COLLATE BINARY`/`NOCASE`/`RTRIM`, `ASC`/`DESC` and `NULLS FIRST`/`NULLS LAST`. The ORDER BY is rebuilt from the matched column name and those fixed keywords. Anything else, including quoted or bracketed names, comments, look-alike Unicode letters and unknown columns, is refused with `extension_schema_violation` and a message that names the rule and the columns the statement does return; put a `LIMIT` in the statement itself. ([#9626](https://github.com/code-yeongyu/oh-my-openagent/pull/9626))
|
|
41
|
+
|
|
42
|
+
**The standalone `omo` binary's `omo setup` carries your OpenCode model choices again.** The release binary printed `could not read the engine's model list (ENOENT ... '/package.json')` and skipped the default model and agent/category picks, and it would have imported a user skill named like a bundled one over the bundled skill. Setup in the binary now reads the provider catalog and bundled skills it ships with; the npm install is unchanged. Thanks to @bluet for the report. ([#10429](https://github.com/code-yeongyu/oh-my-openagent/issues/10429), [#10455](https://github.com/code-yeongyu/oh-my-openagent/pull/10455))
|
|
43
|
+
|
|
44
|
+
**Only you can stop computer use with the stop chord on macOS and Linux X11.** A chord a program sends (the agent's own key presses, `xdotool`, another app posting key events) used to stop every session, and repeated, could keep computer use stopped; it is now ignored, while a press on a real keyboard still stops every running session at once. Wayland's shortcut portal cannot tell the two apart, and Windows follows in a separate change. ([#10444](https://github.com/code-yeongyu/oh-my-openagent/issues/10444), [#10449](https://github.com/code-yeongyu/oh-my-openagent/pull/10449))
|
|
45
|
+
|
|
46
|
+
**On Windows every session can now arm the stop chord, and only a key press on a real keyboard triggers it.** The chord used to be a `RegisterHotKey` registration, which only one process can hold: a second session's engine could not arm it, so its computer use refused input with `StopPathUnavailable`; and a chord a program sent (the agent's own key presses or any other app's `SendInput`) stopped computer use like a real press. A low-level keyboard hook now watches the chord in every session, ignores injected key events, and never swallows a key; one physical press stops every running session at once. ([#10444](https://github.com/code-yeongyu/oh-my-openagent/issues/10444), [#10453](https://github.com/code-yeongyu/oh-my-openagent/pull/10453))
|
|
47
|
+
|
|
48
|
+
**Computer use works on arm64 Linux and Windows.** Each OmO release now carries the desktop engine for Linux arm64 (glibc) and Windows arm64, built on native arm64 machines and checked with its self-test before upload. The compiled OmO binaries for those hosts include the engine, and an npm install downloads it on first use, where it used to report that no engine is built for the host. Musl Linux still has no engine. ([#10440](https://github.com/code-yeongyu/oh-my-openagent/issues/10440), [#10442](https://github.com/code-yeongyu/oh-my-openagent/pull/10442))
|
|
49
|
+
|
|
50
|
+
**A memory-sync account can be recovered on a new device with a passkey.** memory-core can enroll a dedicated recovery passkey and, on a fresh device, use it to rejoin the account: the device's native bridge derives the recovery keys from the passkey and keeps them in the main process, the device walks the served history from the recovery key's own record and pins it, and only a wrap that record vouches for is opened. A wrap the server made up is refused, whatever the server sends. Nothing syncs to a server yet. ([#10412](https://github.com/code-yeongyu/oh-my-openagent/pull/10412))
|
|
51
|
+
|
|
52
|
+
**`herdr agent prompt` reaches OmO panes.** herdr only types into a pane whose foreground process it can name, and the OmO engine runs as `bun …/cli.js`, so every OmO pane was refused with `agent_not_ready`. Under herdr, the interactive engine now starts with `HERDR_AGENT=pi`, the label herdr already shows for these panes. OmO drops it from the environment it hands to anything it starts. Thanks to @devswha for the report. ([#10448](https://github.com/code-yeongyu/oh-my-openagent/issues/10448), [#10452](https://github.com/code-yeongyu/oh-my-openagent/pull/10452))
|
|
53
|
+
|
|
54
|
+
**A memory writer killed mid-commit no longer leaves the next writer stuck on a git lock, and a live commit's lock is never taken.** When a memory writer dies holding a git `index.lock` or ref lock, the writer that takes over its memory-write lock now removes that lock, but only after proving the previous owner dead, seeing no other process still working in the repository (a killed writer's own git command can outlive it), and seeing no process holding the file open. When any of that cannot be proven, including when the open-file check is slow or never answers, the lock stays put and the write fails with an error that names each lock file, says why it was kept, and gives the way out: `/memory unlock <path>` re-checks that one lock, shows what it found, refuses while any live holder, repository user or memory writer is visible, and removes only that file after you confirm (`--force` skips the prompt in a non-interactive session, never a refusal). On Linux desktops and SSH logins where some of your own processes cannot be inspected, the automatic removal does not fire and this command is the way out; [#10322](https://github.com/code-yeongyu/oh-my-openagent/issues/10322) tracks making liveness independent of `/proc`. On Linux this check reads `/proc` directly, so recovery also works on minimal installs and containers without `lsof`. ([#10177](https://github.com/code-yeongyu/oh-my-openagent/issues/10177)) ([#10199](https://github.com/code-yeongyu/oh-my-openagent/pull/10199))
|
|
55
|
+
|
|
56
|
+
**Synced memory refuses a forged or reordered history on the device, the same way the server does.** memory-core now checks every sync object, the hash chain linking them, and the device keyring folded from it, using the format the service ships. A device rejects a record signed by a key that was rotated out or revoked, a dropped or reordered entry, a recovery join from a removed recovery key, and a batch sealed for another account. It treats the server as untrusted: a device trusts an account history only if it starts from the root it pinned during its own pairing or recovery, so a history the server invents, or a different root that looks valid, is refused before any key is opened. A key wrap opens only from a record of that trusted history, and only through the device's own keyring. A missing or damaged pin is treated as no trust at all, and the device asks to re-pair or recover. A replayed batch, a swapped record body, an unsigned row in a vetoed epoch and a row with fields the format does not define are each refused and counted. Nothing syncs to a server yet. ([#10178](https://github.com/code-yeongyu/oh-my-openagent/issues/10178), [#10298](https://github.com/code-yeongyu/oh-my-openagent/pull/10298))
|
|
57
|
+
|
|
58
|
+
**A memory that already exists on a device joins its synced space without losing anything.** Each project's synced memory gets one identity that every device computes the same way from the repository, so two devices enabling sync offline land in one space. When local files meet the space's copy, nothing is deleted: body lines are only added, a list keeps every element from both sides, and a frontmatter value that differs keeps both whole values side by side, never spliced together and never a conflict note in the file. Leftover hub conflict copies are folded in and removed only after a read-back proves every line made it. Nothing syncs to a server yet. ([#10206](https://github.com/code-yeongyu/oh-my-openagent/pull/10206))
|
|
59
|
+
|
|
60
|
+
**`runtime_fallback` moves to the next model when a Cursor pool is spent.** cursor-opencode-provider reports a spent pool as `capacity_limit`, which the fallback classifier did not recognize, so the session ended on the error instead of trying the next rung of the chain. It is now treated like the `resource_exhausted` error it replaces. ([#10170](https://github.com/code-yeongyu/oh-my-openagent/issues/10170), [#10224](https://github.com/code-yeongyu/oh-my-openagent/pull/10224))
|
|
61
|
+
|
|
62
|
+
## [5.1.37] - 2026-10-19
|
|
63
|
+
|
|
64
|
+
5.1.36 was prepared but never published; everything listed for it ships in this release. ([#10552](https://github.com/code-yeongyu/oh-my-openagent/pull/10552))
|
|
65
|
+
|
|
66
|
+
**The engine moves to senpi 2026.10.19: a hung extension handler no longer freezes a session, and queued input survives a crash.** An extension handler that never settles on a lifecycle event is named in the session after 60 s and released after 20 min, or at once on Esc or the turn deadline. Input queued with a client id is written to the session file before it is queued and delivered exactly once. The mouse wheel scrolls the transcript again while a question prompt is waiting, Alt+Q restores queued messages on WSL, and the /btw panel no longer repeats a running eval's output in the scrollback. Anthropic requests of 4096+ characters to `api.anthropic.com` are now sent gzip-compressed, as Claude Code does. ([senpi#3650](https://github.com/code-yeongyu/senpi/issues/3650), [senpi#3549](https://github.com/code-yeongyu/senpi/issues/3549), [senpi#3604](https://github.com/code-yeongyu/senpi/issues/3604), [senpi#3628](https://github.com/code-yeongyu/senpi/issues/3628), [senpi#3649](https://github.com/code-yeongyu/senpi/issues/3649), [senpi#3643](https://github.com/code-yeongyu/senpi/issues/3643), [#10552](https://github.com/code-yeongyu/oh-my-openagent/pull/10552))
|
|
67
|
+
|
|
68
|
+
**Goals keep going while they make progress, and long sessions open and stay light (senpi 2026.10.18-1).** A goal no longer stops after a fixed 150 automatic continuations; it stops only after 60 in a row with no new tool effect, todo change or message from a person, and says why and that any message resumes it. Opening a very large session on a shared host no longer stalls the other sessions, a reopened session keeps only its latest compaction window in memory (four of the largest local transcripts went from 1441 MB to 216 MB of live heap), and reading a very long history over RPC pages instead of dropping the connection. A Claude subscription from `CLAUDE_CODE_OAUTH_TOKEN` or an ambient Claude Code login now shows as a connected subscription, and a host daemon inherits every credential variable the engine reads. Provider accounts can be ordered, and grep starts 5 to 14 `rg` processes where it used to start 63 to 87. ([#10280](https://github.com/code-yeongyu/oh-my-openagent/issues/10280), [#10509](https://github.com/code-yeongyu/oh-my-openagent/pull/10509)) The engine also fixes ChatGPT browser sign-in dead-ending on "State mismatch." when another session holds the callback port ([senpi #3627](https://github.com/code-yeongyu/senpi/pull/3627)), and a compiled engine now finds the Claude Code binary shipped beside its executable instead of failing the lookup ([senpi #3640](https://github.com/code-yeongyu/senpi/pull/3640)).
|
|
69
|
+
|
|
70
|
+
**A client re-attaching to a retained session gets the full task snapshot again, and can pull it on demand.** The task RPC bridge listens for `session_resumed`, which senpi fires when the first client re-attaches to a still-open session, and re-emits the complete `omo.task.updated` snapshot instead of waiting for the next store change to alter its fingerprint; a new `omo.task.list` request answers the same payload for the parent session it names. ([#10016](https://github.com/code-yeongyu/oh-my-openagent/issues/10016), [#10122](https://github.com/code-yeongyu/oh-my-openagent/pull/10122))
|
|
71
|
+
**Memory recall's search index takes a quarter of the memory it did, and a memory write no longer rebuilds it.** Every session that recalls memory keeps a search index of the whole memory. It stored each note's words as a separate table, so a memory of 6,406 notes cost about 231 MB per session, and each memory write threw the index away and rebuilt it, taking 3 to 11 seconds. Words are now numbered once and each note keeps a compact list of numbers that survives a memory write, so the same memory costs 55 MB and a write updates the index in about 16 ms. The notes found and their order are unchanged. ([#10510](https://github.com/code-yeongyu/oh-my-openagent/issues/10510), [#10512](https://github.com/code-yeongyu/oh-my-openagent/pull/10512))
|
|
72
|
+
|
|
73
|
+
**A background task child follows its session when another client replaces it.** A raw RPC client that issues `new_session`, `switch_session` or `fork` on a daemon child's routing handle moves that session to a new durable id and transcript; the child now observes the `session_replaced` record, re-stamps its record's session path and durable id, appends a `replaced` task event, and reattaches to the new transcript instead of the abandoned one. When the replacement stops a turn the child was running, the child ends `interrupted` with the reason and a `task_send` action, and the parent is notified because it did not ask for that stop (a parent's own interrupt stays silent); this needs a senpi that names the cause on `agent_end` (senpi#3357), and an older host keeps the previous error. ([#10018](https://github.com/code-yeongyu/oh-my-openagent/issues/10018), [#10112](https://github.com/code-yeongyu/oh-my-openagent/pull/10112))
|
|
74
|
+
**`unspecified-low` tries GLM 5.3 before GPT.** A user with Claude and Z.ai (or OpenCode Go) but no Xiaomi or xAI provider used to fall from Sonnet 5.5 straight to GPT-6.1 Sol once Sonnet was unavailable. The chain now has `glm-5.3` at high thinking right after Grok 4.7 and ahead of the GPT rungs, so the order for users who hold MiMo or Grok is unchanged. ([#10306](https://github.com/code-yeongyu/oh-my-openagent/issues/10306), [#10311](https://github.com/code-yeongyu/oh-my-openagent/pull/10311))
|
|
75
|
+
|
|
76
|
+
**`omo --help` no longer moves your OpenCode config before you start a session.** On a fresh install `--help` (and `--list-models` / `--list-tips`) loads the omo extension, and its startup migration moved `~/.config/opencode/oh-my-openagent.json` into `~/.omo/migration-backup-*`, so a 4.19.x OpenCode plugin fell back to its defaults. The migration now runs only on launches that can start a session, and `--help` still lists omo's own flags. Thanks to @bluet for the report and @twotnguyen for #10433. ([#10428](https://github.com/code-yeongyu/oh-my-openagent/issues/10428), [#10454](https://github.com/code-yeongyu/oh-my-openagent/pull/10454))
|
|
77
|
+
|
|
78
|
+
**Built, tested and released on Bun 1.4.3.** CI, the release workflows and the devcontainer move from Bun 1.4.2 to 1.4.3, the current stable release, which fixes CVE-2026-48618 (TLS host names) and idle garbage collection in workers. ([#10432](https://github.com/code-yeongyu/oh-my-openagent/pull/10432))
|
|
79
|
+
|
|
80
|
+
**OpenCode edition: Sisyphus names GPT-6.1 Sol and GPT-6 Luna as themselves.** On `gpt-6.1-sol` (and its `-fast`, dashed and Venice `gpt-61-sol` ids) and on `gpt-6-luna`, the Sisyphus and Sisyphus-Junior GPT prompts introduced the model as "a GPT-family model"; they now say "GPT-6.1 Sol" and "GPT-6 Luna". ([#10291](https://github.com/code-yeongyu/oh-my-openagent/issues/10291), [#10292](https://github.com/code-yeongyu/oh-my-openagent/pull/10292))
|
|
81
|
+
|
|
82
|
+
**Memory session shutdown waits for the work it started in the memory directory.** Shutdown now waits, within its existing drain budget, for an in-flight repo maintenance pass and for the status refreshes started at bind and settle, all of which run git inside the identity directory. Before, they could still hold files open after shutdown returned; on Windows the next delete of that directory failed with `EBUSY`/`EFAULT`, which is how the memory pressure-dream test failed intermittently in CI. ([#10154](https://github.com/code-yeongyu/oh-my-openagent/issues/10154), [#10312](https://github.com/code-yeongyu/oh-my-openagent/pull/10312))
|
|
83
|
+
|
|
84
|
+
**Oh My OpenAgent loads on OpenCode 2.** OpenCode 2 refused the plugin with `Plugin must export a default definition with an id and an effect or setup function`, because both entry points exported only the OpenCode 1.x shape. The server and TUI entries now also export an OpenCode 2 `setup` built with `Plugin.define` from `@opencode/plugin`, next to the OpenCode 1.x `server` and `tui` entries, which keep working as before. Each OpenCode 2 start shows a notice that points to OmO Native, with the install command and guide link. ([#8485](https://github.com/code-yeongyu/oh-my-openagent/issues/8485), [#10439](https://github.com/code-yeongyu/oh-my-openagent/pull/10439))
|
|
85
|
+
|
|
86
|
+
**The frontend skill keeps coloured edge stripes off rounded cards, and a linter catches the ones that slip through.** A card with a coloured stripe along one edge, its rounded corners bending the stripe, is one of the most recognizable marks of generated UI, and the skill only banned it when the stripe marked a selected or focused item. It now rules the stripe out before drafting, whether it decorates the card or marks state, and offers what to use instead: a tonal surface, a heading in the accent colour, a glyph, or spacing. The new `check-accent-stripes.ts` script finds the pattern in CSS, Tailwind classes, inline styles, and styled-components, and the skill's checklist runs it before visual QA. Focus rings, form validation, neutral dividers, and tables are left alone. ([#10437](https://github.com/code-yeongyu/oh-my-openagent/issues/10437))
|
|
87
|
+
|
|
88
|
+
**A store extension's `orderBy` can only name columns its statement returns, and its text never reaches the SQL.** `orderBy` used to be pasted into the gateway's ordered read as given, so a `LIMIT`, an `OFFSET` or a column the statement does not select failed as broken SQL (`near "LIMIT": syntax error`, `no such column`). Each comma-separated term must now be one of the statement's own output columns (matched case-insensitively, as SQLite does), optionally with `COLLATE BINARY`/`NOCASE`/`RTRIM`, `ASC`/`DESC` and `NULLS FIRST`/`NULLS LAST`. The ORDER BY is rebuilt from the matched column name and those fixed keywords. Anything else, including quoted or bracketed names, comments, look-alike Unicode letters and unknown columns, is refused with `extension_schema_violation` and a message that names the rule and the columns the statement does return; put a `LIMIT` in the statement itself. ([#9626](https://github.com/code-yeongyu/oh-my-openagent/pull/9626))
|
|
89
|
+
|
|
90
|
+
**The standalone `omo` binary's `omo setup` carries your OpenCode model choices again.** The release binary printed `could not read the engine's model list (ENOENT ... '/package.json')` and skipped the default model and agent/category picks, and it would have imported a user skill named like a bundled one over the bundled skill. Setup in the binary now reads the provider catalog and bundled skills it ships with; the npm install is unchanged. Thanks to @bluet for the report. ([#10429](https://github.com/code-yeongyu/oh-my-openagent/issues/10429), [#10455](https://github.com/code-yeongyu/oh-my-openagent/pull/10455))
|
|
91
|
+
|
|
92
|
+
**Only you can stop computer use with the stop chord on macOS and Linux X11.** A chord a program sends (the agent's own key presses, `xdotool`, another app posting key events) used to stop every session, and repeated, could keep computer use stopped; it is now ignored, while a press on a real keyboard still stops every running session at once. Wayland's shortcut portal cannot tell the two apart, and Windows follows in a separate change. ([#10444](https://github.com/code-yeongyu/oh-my-openagent/issues/10444), [#10449](https://github.com/code-yeongyu/oh-my-openagent/pull/10449))
|
|
93
|
+
|
|
94
|
+
**On Windows every session can now arm the stop chord, and only a key press on a real keyboard triggers it.** The chord used to be a `RegisterHotKey` registration, which only one process can hold: a second session's engine could not arm it, so its computer use refused input with `StopPathUnavailable`; and a chord a program sent (the agent's own key presses or any other app's `SendInput`) stopped computer use like a real press. A low-level keyboard hook now watches the chord in every session, ignores injected key events, and never swallows a key; one physical press stops every running session at once. ([#10444](https://github.com/code-yeongyu/oh-my-openagent/issues/10444), [#10453](https://github.com/code-yeongyu/oh-my-openagent/pull/10453))
|
|
95
|
+
|
|
96
|
+
**Computer use works on arm64 Linux and Windows.** Each OmO release now carries the desktop engine for Linux arm64 (glibc) and Windows arm64, built on native arm64 machines and checked with its self-test before upload. The compiled OmO binaries for those hosts include the engine, and an npm install downloads it on first use, where it used to report that no engine is built for the host. Musl Linux still has no engine. ([#10440](https://github.com/code-yeongyu/oh-my-openagent/issues/10440), [#10442](https://github.com/code-yeongyu/oh-my-openagent/pull/10442))
|
|
97
|
+
|
|
98
|
+
**A memory-sync account can be recovered on a new device with a passkey.** memory-core can enroll a dedicated recovery passkey and, on a fresh device, use it to rejoin the account: the device's native bridge derives the recovery keys from the passkey and keeps them in the main process, the device walks the served history from the recovery key's own record and pins it, and only a wrap that record vouches for is opened. A wrap the server made up is refused, whatever the server sends. Nothing syncs to a server yet. ([#10412](https://github.com/code-yeongyu/oh-my-openagent/pull/10412))
|
|
99
|
+
|
|
100
|
+
**`herdr agent prompt` reaches OmO panes.** herdr only types into a pane whose foreground process it can name, and the OmO engine runs as `bun …/cli.js`, so every OmO pane was refused with `agent_not_ready`. Under herdr, the interactive engine now starts with `HERDR_AGENT=pi`, the label herdr already shows for these panes. OmO drops it from the environment it hands to anything it starts. Thanks to @devswha for the report. ([#10448](https://github.com/code-yeongyu/oh-my-openagent/issues/10448), [#10452](https://github.com/code-yeongyu/oh-my-openagent/pull/10452))
|
|
101
|
+
|
|
102
|
+
**A memory writer killed mid-commit no longer leaves the next writer stuck on a git lock, and a live commit's lock is never taken.** When a memory writer dies holding a git `index.lock` or ref lock, the writer that takes over its memory-write lock now removes that lock, but only after proving the previous owner dead, seeing no other process still working in the repository (a killed writer's own git command can outlive it), and seeing no process holding the file open. When any of that cannot be proven, including when the open-file check is slow or never answers, the lock stays put and the write fails with an error that names each lock file, says why it was kept, and gives the way out: `/memory unlock <path>` re-checks that one lock, shows what it found, refuses while any live holder, repository user or memory writer is visible, and removes only that file after you confirm (`--force` skips the prompt in a non-interactive session, never a refusal). On Linux desktops and SSH logins where some of your own processes cannot be inspected, the automatic removal does not fire and this command is the way out; [#10322](https://github.com/code-yeongyu/oh-my-openagent/issues/10322) tracks making liveness independent of `/proc`. On Linux this check reads `/proc` directly, so recovery also works on minimal installs and containers without `lsof`. ([#10177](https://github.com/code-yeongyu/oh-my-openagent/issues/10177)) ([#10199](https://github.com/code-yeongyu/oh-my-openagent/pull/10199))
|
|
103
|
+
|
|
104
|
+
**Synced memory refuses a forged or reordered history on the device, the same way the server does.** memory-core now checks every sync object, the hash chain linking them, and the device keyring folded from it, using the format the service ships. A device rejects a record signed by a key that was rotated out or revoked, a dropped or reordered entry, a recovery join from a removed recovery key, and a batch sealed for another account. It treats the server as untrusted: a device trusts an account history only if it starts from the root it pinned during its own pairing or recovery, so a history the server invents, or a different root that looks valid, is refused before any key is opened. A key wrap opens only from a record of that trusted history, and only through the device's own keyring. A missing or damaged pin is treated as no trust at all, and the device asks to re-pair or recover. A replayed batch, a swapped record body, an unsigned row in a vetoed epoch and a row with fields the format does not define are each refused and counted. Nothing syncs to a server yet. ([#10178](https://github.com/code-yeongyu/oh-my-openagent/issues/10178), [#10298](https://github.com/code-yeongyu/oh-my-openagent/pull/10298))
|
|
105
|
+
|
|
106
|
+
**A memory that already exists on a device joins its synced space without losing anything.** Each project's synced memory gets one identity that every device computes the same way from the repository, so two devices enabling sync offline land in one space. When local files meet the space's copy, nothing is deleted: body lines are only added, a list keeps every element from both sides, and a frontmatter value that differs keeps both whole values side by side, never spliced together and never a conflict note in the file. Leftover hub conflict copies are folded in and removed only after a read-back proves every line made it. Nothing syncs to a server yet. ([#10206](https://github.com/code-yeongyu/oh-my-openagent/pull/10206))
|
|
107
|
+
|
|
108
|
+
**`runtime_fallback` moves to the next model when a Cursor pool is spent.** cursor-opencode-provider reports a spent pool as `capacity_limit`, which the fallback classifier did not recognize, so the session ended on the error instead of trying the next rung of the chain. It is now treated like the `resource_exhausted` error it replaces. ([#10170](https://github.com/code-yeongyu/oh-my-openagent/issues/10170), [#10224](https://github.com/code-yeongyu/oh-my-openagent/pull/10224))
|
|
109
|
+
|
|
110
|
+
## [5.1.35] - 2026-10-16
|
|
111
|
+
|
|
112
|
+
**The omo-senpi typecheck gives the same result wherever the checkout sits.** It used to pick up `@types/node` from folders above the checkout, so a stray older copy there failed the typecheck while CI passed on the same commit. omo-senpi now declares its own `@types/node` and `bun-types` and reads type packages only from its own `node_modules`. ([#10320](https://github.com/code-yeongyu/oh-my-openagent/issues/10320), [#10332](https://github.com/code-yeongyu/oh-my-openagent/pull/10332))
|
|
113
|
+
|
|
114
|
+
**Memory items sync without losing concurrent edits, even from a stale device.** The sync item model captures a device's edits against the exact snapshot it saw, so a stale capture merges as a concurrent edit instead of silently dropping work or throwing; two captures from one device on one baseline lose nothing, multiline and quoted frontmatter values render as valid YAML on every replica, and path keys that would split one file into several (empty segments, trailing slashes, backslashes, drive letters) are rejected instead of canonicalized. Nothing syncs to a server yet. ([#10203](https://github.com/code-yeongyu/oh-my-openagent/pull/10203))
|
|
115
|
+
|
|
116
|
+
**A `categories` entry named after an agent now gets a warning instead of being silently ignored.** A `task(subagent_type: "plan-consultant")` spawn reads only `agents."plan-consultant"`, so a model chain put under `categories."plan-consultant"` never reached it and the spawn ran the builtin chain, while `omo doctor` still counted that category as usable. Whenever a `categories.<name>` key equals a builtin agent or an agent in your `agents` config, and that agent does not already set its own models, the startup notice and `omo doctor` now say that key is not read for that spawn and show the corrected snippet with your own chain, for example `"agents": { "plan-consultant": { "models": [...] } }`; `agents.<name>.models` stays the one way to set an agent's models. Resolution is unchanged. Thanks to @Bearmancer for the report. ([#10323](https://github.com/code-yeongyu/oh-my-openagent/issues/10323), [#10326](https://github.com/code-yeongyu/oh-my-openagent/pull/10326))
|
|
117
|
+
|
|
118
|
+
**A steering message to a background task is never recorded as both failed and delivered.** When a steer timed out and its retry was still in flight while a later run of the parent reconciled the task's unconfirmed inputs, that run marked the message failed and the retry then delivered it, so the task's log said both. Reconciliation now leaves an input whose retry is in flight to that retry, which records the one real outcome ([#10165](https://github.com/code-yeongyu/oh-my-openagent/pull/10165)).
|
|
119
|
+
|
|
120
|
+
**Memory reflection no longer dies in the sandbox when its session directory does not exist yet.** The Linux sandbox now creates each writable directory it binds (mode 0700) right before bwrap binds it, and every reflection launch first backfills the identity's runtime directories, so an identity created before `runtime/reflection-sessions` existed reflects normally instead of failing with "Can't find source path". An off or unavailable sandbox still creates nothing. Thanks to [@leeseunguk](https://github.com/leeseunguk) and [@AceRothstein71](https://github.com/AceRothstein71) (#7236). ([#7012](https://github.com/code-yeongyu/oh-my-openagent/issues/7012), [#8004](https://github.com/code-yeongyu/oh-my-openagent/pull/8004))
|
|
121
|
+
|
|
122
|
+
**A missing reflection directory no longer gets "turn the sandbox off" advice.** When memory reflection fails because a sandbox bind directory does not exist yet, the notice now names that directory and how to create it, instead of blaming user namespaces and suggesting `memory.reflection.sandbox: "off"`. A real user-namespace refusal still gets the host fix first, with turning the sandbox off only as a last resort. ([#10317](https://github.com/code-yeongyu/oh-my-openagent/issues/10317), [#10319](https://github.com/code-yeongyu/oh-my-openagent/pull/10319))
|
|
123
|
+
|
|
124
|
+
**Opening and closing an OmO session without a message no longer leaves an empty session in `/resume`.** The memory component's hidden session binding is now written as startup bookkeeping, so senpi removes a session that never had a conversation. ([#10168](https://github.com/code-yeongyu/oh-my-openagent/issues/10168), [#10308](https://github.com/code-yeongyu/oh-my-openagent/pull/10308))
|
|
125
|
+
|
|
126
|
+
**A task shard host now exits once its children are done instead of staying alive for days.** When a task child's session parked or closed on its shard, the child's own connection to that shard stayed open, so the shard kept counting the connections and its idle exit never fired; one shard served zero sessions for two days with 18 connections back to itself. Those connections now close when the session ends, and a refused close no longer strands its client ([#10062](https://github.com/code-yeongyu/oh-my-openagent/issues/10062), [#10066](https://github.com/code-yeongyu/oh-my-openagent/pull/10066)).
|
|
127
|
+
|
|
128
|
+
**Computer use can hold keys or a mouse button for a set time and always lets go.** `holdKeys(keys, { duration })` and `holdMouse(x, y, { duration })` hold for up to 100 seconds and release everything they pressed, in reverse order, even when the run is cancelled or the emergency stop fires. ([#9651](https://github.com/code-yeongyu/oh-my-openagent/issues/9651), [#10030](https://github.com/code-yeongyu/oh-my-openagent/pull/10030))
|
|
129
|
+
|
|
130
|
+
**A child task keeps reporting the model it is actually running.** A `set_model` or model cycle applied to a child by any client, and a fallback that later reverts, now update the child's record as they happen: `task_output`, `/tasks`, the desktop task snapshot and the completion notice show the live model, and a switch omo did not make is marked `(switched by operator)`. The persisted record keeps `effective_model.source` within the values earlier builds parse (the live switch detail moves into a nested block those builds ignore), so a downgrade or an older omo sharing the task store still loads these records. ([#10020](https://github.com/code-yeongyu/oh-my-openagent/issues/10020), [#10097](https://github.com/code-yeongyu/oh-my-openagent/pull/10097))
|
|
131
|
+
|
|
132
|
+
**`OMO_DISABLE_TELEMETRY=1` turns OmO Native telemetry fully off, and an opted-out session leaves nothing behind for a later one to send.** Every opt-out (`OMO_DISABLE_TELEMETRY`, `DO_NOT_TRACK`, the PostHog switches and the config flag) now goes through one consent check, taken when the session starts and again before anything is written or hashed: an opted-out session derives no session hash, creates no salt or notice marker, and writes no summary file. Events now carry a time-ordered random `event_id`, are keyed by a hash of the install id instead of the machine (the old machine hash rides along until 2026-12-14 so earlier rows still join), and mark CI runs with a plain `ci` flag. Turning telemetry off while a session is running now stops that session's events within a few seconds, including the raw per-turn row a sampled session kept sending ([#10328](https://github.com/code-yeongyu/oh-my-openagent/issues/10328)). QA drivers that start the real runtime opt out of telemetry through one shared helper. ([#10226](https://github.com/code-yeongyu/oh-my-openagent/pull/10226))
|
|
133
|
+
|
|
134
|
+
Fixed: OmO Native versions v5.0.0-beta.6 through v5.1.34 kept sending usage events for the rest of a session after telemetry was turned off while that session was running ([#10328](https://github.com/code-yeongyu/oh-my-openagent/issues/10328)). These events are personless and carry a session hash; the ones confirmed in that report are `turn_completed`, `feature_used` and `computer_use_activation`, other usage events may have been sent the same way, and like every OmO Native event they never contain prompt or response text, file paths, or file contents. Turning telemetry off before a session started, or through one of the opt-out environment variables, was never affected. Since v5.1.35 consent is checked when events are sent (a check is reused for at most 5 seconds), and events already queued are dropped once the opt-out is read. Those events can't be told apart from consented ones, so erasure is per install: send a private request through this repository's [private vulnerability reporting form](https://github.com/code-yeongyu/oh-my-openagent/security/advisories/new), titled "Telemetry erasure request", with your install id (the file `omo-senpi/omo-native/install-id` under the agent home), and an operator erases that install's events. Put the id only in that private report, never in a public issue or comment. Events from older versions that are keyed only by the machine hash are erased along with your install's events only where our records pair that hash with your install id and no other install. A history shared by several machines or installs (the same hostname hash) is not erased that way: name it in the private request, and it is erased only if the request proves the events are yours. ([#10226](https://github.com/code-yeongyu/oh-my-openagent/pull/10226))
|
|
135
|
+
|
|
136
|
+
**Sessions load about 33 KB less code at startup, because the memory palace and `/people` now load on first use.** The `/palace` viewer generator and the `/people` roster, record and `--ask` runtime moved out of the entry bundle into their own file, which is loaded the first time either command runs, the same way `/doctor` and `/memfs` already work. Both commands print and notify exactly what they did before. If that file is missing or broken, the command now says so and tells you to reinstall omo and start a new session, instead of failing silently. ([#10300](https://github.com/code-yeongyu/oh-my-openagent/pull/10300))
|
|
137
|
+
|
|
138
|
+
**The memory sync device keyring refuses a few more malformed inputs instead of failing late.** A keyring record can no longer carry the snapshot tag as its type, deeply nested JSON (over 64 levels) is refused with a typed error instead of a stack overflow, the wrap associated data goes through the one RFC 8785 serializer and refuses a non-integer epoch, and a low-order X25519 point is refused with the normal "does not open" error. Follow-up to the review of [#10194](https://github.com/code-yeongyu/oh-my-openagent/pull/10194) ([#10274](https://github.com/code-yeongyu/oh-my-openagent/pull/10274)).
|
|
139
|
+
|
|
140
|
+
**Background task reattachment now recovers the real result instead of starting duplicate work.** A parent that reloads or loses its host connection checks whether the child is still running before reading its transcript. Finished turns are adopted once, interrupted turns continue once, and a turn stopped by its deadline stays interrupted until an explicit `task_send`. ([#10012](https://github.com/code-yeongyu/oh-my-openagent/issues/10012), [#10013](https://github.com/code-yeongyu/oh-my-openagent/issues/10013), [#10078](https://github.com/code-yeongyu/oh-my-openagent/pull/10078)).
|
|
141
|
+
|
|
142
|
+
**The ulw-research report checker no longer counts an invisible image as the report's visual.** An image or chart with any zero dimension, such as `width="600" height="0"` or an inline `height: 0`, no longer satisfies the rule that every final report carries a visual; one with no declared size still counts, since the browser sizes it. ([#10266](https://github.com/code-yeongyu/oh-my-openagent/issues/10266), [#10268](https://github.com/code-yeongyu/oh-my-openagent/pull/10268))
|
|
143
|
+
|
|
144
|
+
**Computer use no longer asks to confirm foreground control in full access mode.** A full-access session grants it directly and shows a note with the reason; other modes still ask, headless runs still never grant, and the grant still ends with the turn, on release or on the stop chord. Reported by Q. ([#10022](https://github.com/code-yeongyu/oh-my-openagent/issues/10022), [#10024](https://github.com/code-yeongyu/oh-my-openagent/pull/10024))
|
|
145
|
+
|
|
146
|
+
**`omo update` no longer offers to overwrite a binary your package manager installed.** When the compiled `omo` is owned by pacman, dpkg, rpm, Homebrew or Nix, `omo update` used to print a `curl … && mv` replace command that either failed on a root-owned path or left the package manager tracking a file it no longer installed. It now exits non-zero and names the package and the manager's own upgrade command (for example `brew upgrade omo`). The ownership probe fails open: a missing tool or an unowned path keeps the old behavior. Thanks to @cynkai. ([#9585](https://github.com/code-yeongyu/oh-my-openagent/issues/9585), [#9629](https://github.com/code-yeongyu/oh-my-openagent/pull/9629))
|
|
147
|
+
|
|
148
|
+
**Memory reflection notices name the failing run's log by its real path.** The parked notice, the reflection-failing health alert and `/memory doctor` told you to inspect `runtime/reflection-sessions/<runId>/child-stderr.log`, a relative path with a literal `<runId>` under a directory runs are not kept in. They now name the newest failing run's absolute `child-stderr.log` (home shortened to `~`), and when that run was already pruned they say so and point at the stderr tail kept in the failure detail. Thanks to @cynkai. ([#9497](https://github.com/code-yeongyu/oh-my-openagent/issues/9497), [#9505](https://github.com/code-yeongyu/oh-my-openagent/pull/9505))
|
|
149
|
+
|
|
10
150
|
## [5.1.34] - 2026-10-16
|
|
11
151
|
|
|
12
152
|
**`oracle`, `momus` and `plan-reviewer` fall back to Argon before the previous pro model.** In all three chains, Argon at high thinking (Google, Vertex or OpenCode) now sits directly ahead of the previous pro rung, which stays in place for Copilot and for engine pins that do not serve Argon yet. The GPT heads keep first place: on 8 consult and plan-review tasks graded blind, Argon beat the previous pro model in all 16 judgments but did not beat the current GPT heads on correctness, and its median answer took 57 s against 15 s. Telemetry now names the Qwen 3.7 Plus rung of `explore` and `librarian` instead of reporting it as `custom`. ([#10180](https://github.com/code-yeongyu/oh-my-openagent/issues/10180), [#10263](https://github.com/code-yeongyu/oh-my-openagent/pull/10263))
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// omo:
|
|
1
|
+
// omo:N7yV2WZOIOdRnwwCEF2QqkFTSVMebOlQ84G7HpOZFBE:5LLt7853QZ0R275mM__hYa8UeDUj-ySee_PqmOY8R7U
|
|
2
2
|
function e(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">")}function r(r,s){let i=[`<operating-rules version="${e(r).replaceAll('"',""")}">`];for(let r of s)i.push(`- ${e(r)}`);return i.push("</operating-rules>"),i.join("\n")}function s(e,r){if(null===e||"object"!=typeof e||Array.isArray(e))throw Error(`${r} must be an object`);return e}function i(e,r){let s=e[r];if("string"!=typeof s||0===s.length)throw Error(`${r} must be a non-empty string`);return s}function o(e){let r=s(e,"a rules target"),o=i(r,"session_durable_id"),n=r.binding_id;if(null!=n&&"string"!=typeof n)throw Error("binding_id must be a string");let t=r.behavioral;if(null===t)return{session_durable_id:o,binding_id:n??void 0,behavioral:null};if(!Array.isArray(t)||!t.every(e=>"string"==typeof e))throw Error("behavioral must be an array of rule texts, or null to clear the session's rules");return{session_durable_id:o,binding_id:n??void 0,behavioral:t}}function n(e,r){let o=i(s(r,"blockForSession args"),"session_durable_id"),n=e.one(["version","block"],"SELECT version, block FROM gateway_rules_blocks WHERE session_durable_id = ?",[o]);return void 0===n?null:{version:String(n.version),block:String(n.block)}}function t(e,r){let o=i(s(r,"sessionsWithRules args"),"scope");return{sessions:e.all(["session_durable_id","version"],"SELECT session_durable_id, version FROM gateway_rules_blocks WHERE scope = ?",[o],"session_durable_id").map(e=>({session_durable_id:String(e.session_durable_id),version:String(e.version)}))}}async function l(n,t){let l=s(t,"rulesCommitted args"),a=i(l,"scope"),d=i(l,"version"),u=l.now??Date.now();if("number"!=typeof u||!Number.isFinite(u))throw Error("now must be a finite timestamp");if(!Array.isArray(l.targets))throw Error("targets must be an array");let _=[];for(let s of l.targets){let i=o(s);if(null===i.behavioral){let e=n.exec("DELETE FROM gateway_rules_blocks WHERE session_durable_id = ?",[i.session_durable_id]);_.push({session_durable_id:i.session_durable_id,outcome:e>0?"removed":"absent"});continue}let t=r(d,i.behavioral),l=n.exec("INSERT INTO gateway_rules_blocks (session_durable_id, scope, version, block, updated_at) VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(session_durable_id) DO UPDATE SET scope = excluded.scope, version = excluded.version, block = excluded.block, updated_at = excluded.updated_at\n WHERE gateway_rules_blocks.version != excluded.version",[i.session_durable_id,a,d,t,u]);if(void 0===i.binding_id){_.push({session_durable_id:i.session_durable_id,outcome:l>0?"updated":"unchanged"});continue}let c=await n.enqueue({binding_id:i.binding_id,event_id:`rules_changed:${a}:${d}:${i.session_durable_id}`,text:[`<operating-rules-changed scope="${e(a).replaceAll('"',""")}" version="${e(d).replaceAll('"',""")}">`,"The operating rules for this gateway scope changed; this session's system prompt carries the new block from its next turn.","</operating-rules-changed>"].join("\n"),mode:"follow_up"});_.push({session_durable_id:i.session_durable_id,outcome:l>0?"updated":"unchanged",delivery:"ok"===c.kind?{kind:"ok",delivery_id:c.delivery_id,deduplicated:!0===c.deduplicated}:{kind:"error",error:c.error}})}return{scope:a,version:d,outcomes:_}}export{n as blockForSession,l as rulesCommitted,t as sessionsWithRules};
|