omo-ai 5.1.40 → 5.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/lib/launcher.js +7 -0
- package/bin/lib/legacy-service-credentials.js +36 -0
- package/bin/lib/model-chains.js +110 -0
- package/bin/lib/provider-map.json +1 -1
- package/package.json +2 -2
- package/plugin/CHANGELOG.md +53 -0
- package/plugin/extensions/dream-persona.md +2 -2
- package/plugin/extensions/facts-persona.md +12 -1
- package/plugin/extensions/gateway-rules-extension.mjs +1 -1
- package/plugin/extensions/gateway-store-worker.mjs +1 -1
- package/plugin/extensions/memory-run-supervisor.mjs +1 -1
- package/plugin/extensions/omo-claim-receipt.js +2 -0
- package/plugin/extensions/omo-computer-use.js +2 -2
- package/plugin/extensions/omo-feedback.js +2 -0
- package/plugin/extensions/omo-init-deep-advisor.js +1 -1
- package/plugin/extensions/omo-member.js +2 -2
- package/plugin/extensions/omo-memory-doctor.js +1 -1
- package/plugin/extensions/omo-memory-memfs.js +1 -1
- package/plugin/extensions/omo-memory-palace.js +2 -2
- package/plugin/extensions/omo-memory-sync.js +2 -2
- package/plugin/extensions/omo-onboarding-scan.js +2 -2
- package/plugin/extensions/omo-recall-store.js +1 -1
- package/plugin/extensions/omo-session-history.js +1 -1
- package/plugin/extensions/omo-side-panel.js +2 -2
- package/plugin/extensions/omo-task.js +2 -2
- package/plugin/extensions/omo-telemetry-posthog.js +1 -1
- package/plugin/extensions/omo-thread-client.js +1 -1
- package/plugin/extensions/omo.js +2 -2
- package/plugin/extensions/reflection-persona.md +1 -1
- package/plugin/package.json +3 -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 +645 -4
- package/plugin/runtime/lsp-daemon/dist/.omo-runtime-manifest.json +4 -4
- package/plugin/runtime/lsp-daemon/dist/cli.js +10 -0
- package/plugin/runtime/lsp-daemon/dist/client.js +10 -0
- package/plugin/runtime/lsp-daemon/dist/index.js +10 -0
- package/plugin/runtime/rollback-migrate.js +2 -2
- package/plugin/runtime/task-config/index.js +2 -2
- package/plugin/runtime/thread-sdk/sdk.js +2 -2
- package/plugin/scripts/install.mjs +2 -0
- package/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/plugin/skills/frontend/SKILL.md +6 -6
- package/plugin/skills/frontend/references/design/README.md +2 -2
- package/plugin/skills/frontend/references/perfection/README.md +2 -2
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/declarations.ts +22 -13
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/findings.ts +7 -6
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/lint.ts +18 -24
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/markup.ts +14 -7
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/selector.ts +5 -1
- package/plugin/skills/frontend/scripts/perfection/accent-stripes/tailwind.ts +32 -14
- package/plugin/skills/frontend/scripts/perfection/check-accent-stripes.ts +1 -1
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/inline-ok.tsx +1 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/neutral-and-flat.css +1 -1
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/tailwind-ok.tsx +5 -2
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/negative/uniform-and-rings.css +2 -2
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/accent-outlines.css +4 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/accent-outlines.tsx +8 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/{negative → positive}/flat-stripe-side.tsx +3 -1
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/report-callouts.html +8 -0
- package/plugin/skills/frontend/scripts/perfection/fixtures/accent-stripes/positive/square-surfaces.css +8 -0
- package/plugin/skills/ulw-research/SKILL.md +10 -2
- package/plugin/skills/ulw-research/references/deliverable-phase.md +10 -0
- package/plugin/skills/ulw-research/references/report-visualization.md +23 -0
- package/plugin/skills/visualize/SKILL.md +4 -3
package/bin/lib/launcher.js
CHANGED
|
@@ -4,6 +4,7 @@ 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"
|
|
7
|
+
import { runDoctorModels } from "./model-chains.js"
|
|
7
8
|
import { doctorComputerUseLines } from "./computer-use-doctor.js"
|
|
8
9
|
import { doctorConfigLines } from "./config-doctor.js"
|
|
9
10
|
import { runDaemonCommand } from "./daemon.js"
|
|
@@ -12,6 +13,7 @@ import { isHostStatusAll, runHostStatusAll, sessionActivityReader } from "./host
|
|
|
12
13
|
import { runDoctor } from "./doctor.js"
|
|
13
14
|
import { ensureEnginePrepared, preparePluginLaunchSpec } from "./engine-prepare.js"
|
|
14
15
|
import { migrateLegacyBunGlobalManifest } from "./legacy-bun-global-migration.js"
|
|
16
|
+
import { removeLegacyServiceCredentials } from "./legacy-service-credentials.js"
|
|
15
17
|
import { adoptLegacyFlatState, canonicalAgentDir } from "./agent-dir.js"
|
|
16
18
|
import { senpiEnvironment } from "./engine-environment.js"
|
|
17
19
|
import { withHerdrAgentHint } from "./herdr-agent-hint.js"
|
|
@@ -130,6 +132,7 @@ export function rollbackMigrateCall(request) {
|
|
|
130
132
|
|
|
131
133
|
export async function runLauncher(args = process.argv.slice(2)) {
|
|
132
134
|
migrateLegacyBunGlobalManifest()
|
|
135
|
+
removeLegacyServiceCredentials()
|
|
133
136
|
reportLegacyFlatAdoption()
|
|
134
137
|
const command = args[0]
|
|
135
138
|
// The toolkit CLI is no longer part of the Native payload; the loop is driven in-process by the
|
|
@@ -177,6 +180,10 @@ export async function runLauncher(args = process.argv.slice(2)) {
|
|
|
177
180
|
if (command === "doctor") {
|
|
178
181
|
// Doctor is a launch too: it reports only what the launch-time preparation could not fix.
|
|
179
182
|
preparePluginLaunchSpec({ pluginRoot: join(packageRoot, "plugin") })
|
|
183
|
+
if (args[1] === "--models") {
|
|
184
|
+
process.exitCode = await runDoctorModels(args.slice(2), { agentDir: canonicalAgentDir() })
|
|
185
|
+
return
|
|
186
|
+
}
|
|
180
187
|
const [categoryCoverage, computerUse, configDiagnostics, gateway] = args[1] === "--reap"
|
|
181
188
|
? [[], [], [], []]
|
|
182
189
|
: await Promise.all([
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { lstatSync, readFileSync, unlinkSync } from "node:fs"
|
|
2
|
+
import { homedir } from "node:os"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
|
|
5
|
+
// An intermediate sign-in build left device private keys and refresh material in a plaintext
|
|
6
|
+
// `~/.omo/credentials.json`, sometimes world-readable (#10697). Credentials now live only in the OS
|
|
7
|
+
// keystore, so nothing reads this file; remove it on launch. Only the exact legacy shape is removed:
|
|
8
|
+
// a regular file (never a symlink or FIFO) whose JSON object carries both private keys.
|
|
9
|
+
const MAX_LEGACY_BYTES = 64 * 1024
|
|
10
|
+
|
|
11
|
+
export function removeLegacyServiceCredentials(home = process.env.HOME || process.env.USERPROFILE || homedir()) {
|
|
12
|
+
const path = join(home, ".omo", "credentials.json")
|
|
13
|
+
let stats
|
|
14
|
+
try {
|
|
15
|
+
stats = lstatSync(path)
|
|
16
|
+
} catch {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
if (!stats.isFile() || stats.size > MAX_LEGACY_BYTES) return false
|
|
20
|
+
|
|
21
|
+
let parsed
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(readFileSync(path, "utf8"))
|
|
24
|
+
} catch {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return false
|
|
28
|
+
if (!("signingPrivateKey" in parsed) || !("sealingPrivateKey" in parsed)) return false
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
unlinkSync(path)
|
|
32
|
+
} catch {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `omo doctor --models [--json]`: per category and task agent, the model a spawn runs first, the
|
|
3
|
+
* runtime retry chain in order, and every declared rung with its source (built-in, omo.json, the
|
|
4
|
+
* category an agent borrows) and whether it runs, then the main session's model_profile lane.
|
|
5
|
+
*
|
|
6
|
+
* The answer comes from the same places `omo doctor`'s coverage line does: the pinned engine's
|
|
7
|
+
* offline model list (`engineAvailableModels`: auth.json, models.json, env keys; no network, nothing
|
|
8
|
+
* written) classified by the spawn resolvers through the staged category-coverage runtime bundle.
|
|
9
|
+
* Unlike the coverage line this is an explicit request, so a runtime that cannot answer fails it.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { COVERAGE_RUNTIME, engineAvailableModels } from "./category-coverage.js"
|
|
13
|
+
import { packageRoot } from "./package-paths.js"
|
|
14
|
+
import { join } from "node:path"
|
|
15
|
+
import { pathToFileURL } from "node:url"
|
|
16
|
+
|
|
17
|
+
const STATE_TEXT = {
|
|
18
|
+
primary: () => "runs first",
|
|
19
|
+
excluded: (rung) => `excluded by exclude_models "${rung.excludedBy}"`,
|
|
20
|
+
"no-login": () => "no login",
|
|
21
|
+
"not-reached": () => "not reached",
|
|
22
|
+
applied: () => "applied",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function rungModel(rung) {
|
|
26
|
+
return rung.providers === undefined ? rung.model : `${rung.model} (${rung.providers.join(" | ")})`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function rungLines(rungs) {
|
|
30
|
+
let fallback = 0
|
|
31
|
+
const width = Math.max(0, ...rungs.map((rung) => rungModel(rung).length))
|
|
32
|
+
return rungs.map((rung, index) => {
|
|
33
|
+
const state = rung.state === "fallback" ? `fallback ${(fallback += 1)}` : STATE_TEXT[rung.state](rung)
|
|
34
|
+
return ` ${index + 1}. ${rungModel(rung).padEnd(width)} ${rung.source.padEnd(9)} ${state}`
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function entryLines(entry) {
|
|
39
|
+
const head = entry.status === "resolved"
|
|
40
|
+
? ` ${entry.name}: ${entry.runtime.join(" -> ")}`
|
|
41
|
+
: ` ${entry.name}: ${entry.status}${entry.reason === undefined ? "" : ` (${entry.reason})`}`
|
|
42
|
+
return [head, ...rungLines(entry.rungs)]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The human report. @param {any} view the runtime's `modelChains` result */
|
|
46
|
+
export function formatModelChainLines(view) {
|
|
47
|
+
const profile = view.modelProfile
|
|
48
|
+
const lane = profile.selection === "set" ? `model_profile ${profile.profile}` : `model_profile unset, default lane ${profile.profile}`
|
|
49
|
+
return [
|
|
50
|
+
"Models a spawn resolves right now (logins read offline; credentials are not probed).",
|
|
51
|
+
`exclude_models: ${view.excludeModels.length === 0 ? "none" : view.excludeModels.join(", ")}`,
|
|
52
|
+
...(view.configProfile === undefined ? [] : [`config profile: ${view.configProfile}`]),
|
|
53
|
+
"",
|
|
54
|
+
"categories",
|
|
55
|
+
...view.categories.flatMap(entryLines),
|
|
56
|
+
"",
|
|
57
|
+
"agents",
|
|
58
|
+
...view.agents.flatMap(entryLines),
|
|
59
|
+
"",
|
|
60
|
+
`main session (${lane})`,
|
|
61
|
+
` ${profile.applied ?? `none${profile.reason === undefined ? "" : ` (${profile.reason})`}`}`,
|
|
62
|
+
...rungLines(profile.rungs),
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function loadRuntime() {
|
|
67
|
+
return import(pathToFileURL(join(packageRoot, COVERAGE_RUNTIME)).href)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @typedef {{
|
|
72
|
+
* agentDir: string,
|
|
73
|
+
* json: boolean,
|
|
74
|
+
* cwd?: string,
|
|
75
|
+
* env?: Record<string, string | undefined>,
|
|
76
|
+
* loadEngine?: () => Promise<any>,
|
|
77
|
+
* loadRuntime?: () => Promise<any>,
|
|
78
|
+
* }} DoctorModelsOptions
|
|
79
|
+
* @param {DoctorModelsOptions} options
|
|
80
|
+
* @returns {Promise<{ output: string, failed: boolean }>}
|
|
81
|
+
*/
|
|
82
|
+
export async function doctorModelChains(options) {
|
|
83
|
+
try {
|
|
84
|
+
const runtime = await (options.loadRuntime ?? loadRuntime)()
|
|
85
|
+
const cwd = options.cwd ?? process.cwd()
|
|
86
|
+
const models = await engineAvailableModels({ agentDir: options.agentDir, cwd, loadEngine: options.loadEngine })
|
|
87
|
+
const view = runtime.modelChains({ models, cwd, env: options.env ?? process.env })
|
|
88
|
+
return { output: options.json ? JSON.stringify(view, null, 2) : formatModelChainLines(view).join("\n"), failed: false }
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return { output: `FAIL models: ${error instanceof Error ? error.message : String(error)}`, failed: true }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Prints the report and returns the exit code: 0, 1 when the runtime cannot answer, 2 for an option
|
|
96
|
+
* other than `--json`.
|
|
97
|
+
* @param {string[]} args the arguments after `--models`
|
|
98
|
+
* @param {Omit<DoctorModelsOptions, "json">} options
|
|
99
|
+
* @returns {Promise<number>}
|
|
100
|
+
*/
|
|
101
|
+
export async function runDoctorModels(args, options) {
|
|
102
|
+
const unknown = args.filter((arg) => arg !== "--json")
|
|
103
|
+
if (unknown.length > 0) {
|
|
104
|
+
console.error(`omo doctor --models: unknown option ${unknown.join(" ")} (accepted: --json)`)
|
|
105
|
+
return 2
|
|
106
|
+
}
|
|
107
|
+
const result = await doctorModelChains({ ...options, json: args.includes("--json") })
|
|
108
|
+
console.log(result.output)
|
|
109
|
+
return result.failed ? 1 : 0
|
|
110
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "Derived from @code-yeongyu/senpi 2026.10.
|
|
2
|
+
"_comment": "Derived from @code-yeongyu/senpi 2026.10.21-2 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",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omo-ai",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.42",
|
|
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.21-2"
|
|
21
21
|
},
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
package/plugin/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [5.1.42] - 2026-10-22
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
**Memory sync checks each new keyring entry without re-reading the chain before it.** The client now uses the shared receiver's incremental entry point (omo-service #600, re-vendored), and the receiver moves along with the verified chain state. Appending one entry to a 1,000-entry chain used to hand the receiver all 1,006 entries again; it now hands it one, at any chain length. What the receiver refuses is unchanged, including a replayed or lower-seq entry. ([#10694](https://github.com/code-yeongyu/oh-my-openagent/pull/10694), fixes [#10655](https://github.com/code-yeongyu/oh-my-openagent/issues/10655))
|
|
14
|
+
|
|
15
|
+
**The engine moves to senpi 2026.10.21-2: idle task hosts exit again, reopened sessions stop spending quota, and sessions share their prompt cache ([#10893](https://github.com/code-yeongyu/oh-my-openagent/pull/10893)).** A monitor watch no longer keeps every task shard host on the machine running, so idle hosts exit as designed. Reopening an unattended session no longer spends quota on goals or background events until you send input or choose Resume. Responses prompt-cache keys follow the model, instructions and tool schemas instead of the session, so new sessions and task children with the same prefix reuse one cache. A goal waiting on a subscription limit retries at the provider's reset time instead of hammering the spent account. On Windows, input sent while a turn is running no longer gets stuck or races through the whole fallback chain. Switching a long conversation to a smaller-context model while the current one is at its limit now applies at the next message, saving an API key over an OAuth login (or the reverse) replaces the credential instead of storing one that is never used, a busy command monitor is no longer muted after 200 lines a day, and every assistant reply records its timing (time to first token and generation rate).
|
|
16
|
+
|
|
17
|
+
**OmO records when an answer says "tests pass" or "done" with nothing in the session behind it.** When a turn's final answer says tests pass, a build or typecheck is clean, a feature already exists, or the work is done, OmO checks the session for a check that passed after the last code edit, and records in the session whether the claim is backed. It starts in shadow mode: nothing is shown to you or the agent and no turn is blocked or continued, because on a week of our own sessions most flags were still false positives. Setting the `omo-senpi-claim-receipt-nudge` flag turns on a short note under a flagged answer (once per message you send) and passes the flagged claims to a gate reviewer spawned afterwards. ([#10785](https://github.com/code-yeongyu/oh-my-openagent/pull/10785))
|
|
18
|
+
|
|
19
|
+
**`omo doctor --models` shows which model every task category and agent runs, and `exclude_models` keeps a model out of all of them ([#10763](https://github.com/code-yeongyu/oh-my-openagent/pull/10763)).** The new doctor view prints, per category and agent, the model a spawn runs first, the retry chain in order, and each rung's source (built-in, omo.json, or the category an agent borrows) with why it does or does not run: excluded, no login, or not reached. It reads the same resolvers spawns use, and `--json` gives the same view to scripts. The new top-level `exclude_models` list (`provider/model`, a bare model id, or a `*` pattern such as `"claude-fable-*"`) is honored by category and agent chains, memory children (the session model they would inherit included), the main session's model profile, explicit task pins, and task children respawned or resumed on a recorded model, including their recorded fallback chains. Before, no setting could stop a builtin rung from running; only logging out of its provider did (#10759).
|
|
20
|
+
|
|
21
|
+
**Geeky becomes Obsessive.** The two GPT lanes' display names are now Obsessive · Normal and Obsessive · Heavy, with one-line descriptions to match and a shared caution, NOT RECOMMENDED FOR WRITING OR EVERYDAY WORK., that the session-start notice renders in bold; the `geeky-normal` and `geeky-heavy` ids and the `family` they report stay unchanged, so a saved `model_profile`, a picker choice or a user overlay keeps resolving exactly as before. A `model_profiles` entry may set its own `caution` string, and one that replaces a builtin inherits its caution when it does not. ([#10608](https://github.com/code-yeongyu/oh-my-openagent/pull/10608), [#10590](https://github.com/code-yeongyu/oh-my-openagent/issues/10590))
|
|
22
|
+
|
|
23
|
+
**Obsessive · Normal leads with GPT-6 Astra at medium.** The `geeky-normal` model profile resolves GPT-6 Astra at medium on every GPT provider first, with its GPT-6.1 Sol Fast, GPT-6.1 Sol and GPT-5.6 Sol rungs kept as availability fallbacks; Obsessive · Heavy keeps Astra at high. ([#10608](https://github.com/code-yeongyu/oh-my-openagent/pull/10608), [#10590](https://github.com/code-yeongyu/oh-my-openagent/issues/10590))
|
|
24
|
+
|
|
25
|
+
**Memory sync can verify and seal real batches.** The sync engine now has a production verifier and sealer: each row another device appended is checked against this device's verified copy of the account chain, then opened with the epoch key the device holds, and only that memory's own notes reach it. Outgoing batches are encrypted, signed and linked the same way. Nothing uses this yet; the device sign-in that opens a session comes next. ([#10625](https://github.com/code-yeongyu/oh-my-openagent/issues/10625), [#10627](https://github.com/code-yeongyu/oh-my-openagent/pull/10627))
|
|
26
|
+
|
|
27
|
+
**A signed-in device now reaches memory sync.** Memory sync used to stay idle on every device, because nothing gave it the device's sign-in. It now reads the device `omo login` signed in, and the key pair that device registered, then opens the account's sync for that memory. A device that is signed in but has not joined the account's memory yet says so instead of reporting no sign-in. When the device signs out or is removed, sync closes and goes back to idle. ([#10625](https://github.com/code-yeongyu/oh-my-openagent/issues/10625), [#10630](https://github.com/code-yeongyu/oh-my-openagent/pull/10630))
|
|
28
|
+
|
|
29
|
+
**A subtask whose category has no model left now finishes on the model your session runs.** When every model in a delegated task's category is out (its usage limit is spent or its provider is not signed in), the task continues on your session's model in the same conversation, so tool calls it already finished are not run again. Before, it stopped with a usage-limit error even though your own model was working. The task's row, `task_output` and the completion notice say which model it ran on and why, for example `parent-model:usage limit, resets 3:20am`, and name any tool call the limit cut off, which never ran. Categories with a model that still works route exactly as before, and a session model on the same spent account still ends with the old error. Until the engine can limit this to spent or signed-out models, a task whose category models all fail for another reason, such as an overload, also continues on your session's model and says so ([#10750](https://github.com/code-yeongyu/oh-my-openagent/issues/10750), [#10772](https://github.com/code-yeongyu/oh-my-openagent/pull/10772)).
|
|
30
|
+
|
|
31
|
+
**Git worktrees of one repository can share the main checkout's memory.** Set `memory.auto_identity: "git"` (for example in `~/.omo/omo.jsonc`, so worktrees created outside the main checkout see it) and every linked worktree, including the ones Orca creates per agent, reads and writes the main checkout's memory instead of starting empty. The main checkout keeps its existing memory, sessions that are already bound keep theirs, and the default `"path"` leaves behavior unchanged. ([#9659](https://github.com/code-yeongyu/oh-my-openagent/issues/9659), [#10047](https://github.com/code-yeongyu/oh-my-openagent/pull/10047))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## [5.1.41] - 2026-10-22
|
|
35
|
+
|
|
36
|
+
**The engine moves to senpi 2026.10.21-1: engine hosts start faster and idle far more quietly, Claude subscriptions use the native 1M context again, and desktop threads are lighter ([#10740](https://github.com/code-yeongyu/oh-my-openagent/pull/10740)).** A new engine host starts about 0.1 s faster, and an idle engine host wakes 405 times per 90 s instead of 1,106. Current Claude subscription models (Fable 5, Sonnet 5, Haiku 5.5, Opus 4.7 and later) get their native 1M window back without asking for extra usage, and the Claude subscription lane runs Claude Code 2.1.306. Each desktop thread's engine supervisor starts from its own small program instead of the whole CLI, and messages and tool cards no longer keep duplicate copies (about 640 bytes per message and 2 KB per tool card). Models that models.dev marks deprecated stay available, labeled deprecated, instead of disappearing (GitHub Copilot Claude Opus 4.7, GPT-5.5 and GPT-5.4, Baseten Kimi K2 and others), and the model catalog is refreshed; some models get updated thinking levels (for example Nemotron 3 Ultra: off is now none). A message that was already delivered no longer stays listed under Steering, and with several accounts for one provider a usage limit stops failing turns once its reset has passed; a quota failure now says when it resets.
|
|
37
|
+
|
|
38
|
+
**Memory sync refuses signatures under weak Ed25519 keys.** Bun's WebCrypto accepts a signature made under a small-order public key, such as the identity key, for any message. The sync client now checks every key and signature it verifies itself, and refuses any keyring record that would bring a weak or malformed signing key into the account's keyring, including keys that appear only inside a fork-resolution's evidence. ([#10743](https://github.com/code-yeongyu/oh-my-openagent/pull/10743), fixes [#10742](https://github.com/code-yeongyu/oh-my-openagent/issues/10742))
|
|
39
|
+
|
|
40
|
+
**Outside full access, computer use asks before it acts on your screen as a whole.** Clicks, typing, key presses, scrolling, drags, pointer moves and holds aimed at the desktop or a display land on your real pointer and frontmost app, so they now need the same "Allow foreground computer control?" approval as foreground input; without it they fail with `ControlRequired` and nothing is sent. Input to one window in the background still needs no approval, and full access keeps working without a prompt, headless runs included. The macOS background check no longer moves your pointer back after you moved it, and a stopped session stays stopped when the desktop engine restarts. ([#10788](https://github.com/code-yeongyu/oh-my-openagent/issues/10788), [#10790](https://github.com/code-yeongyu/oh-my-openagent/pull/10790))
|
|
41
|
+
|
|
42
|
+
**OmO counts how often you had to repeat a standing instruction ([#10780](https://github.com/code-yeongyu/oh-my-openagent/issues/10780), [#10786](https://github.com/code-yeongyu/oh-my-openagent/pull/10786)).** Rules like "never filter the monitor output" now live in `system/agreements.md` with a count. When you correct the agent on something an agreement already covers, the count goes up; after the third time (`memory.agreements.needs_human_threshold`) the agent is told the rule keeps being broken and you see a notice. `/agreements` lists them and lets you add your own, acknowledge one, or drop one; the agreements you add can only be changed by you. The design is inspired by dinobot by roach (PolyForm Noncommercial 1.0.0); no dinobot code was used.
|
|
43
|
+
|
|
44
|
+
**The launcher deletes a legacy `~/.omo/credentials.json` left by an intermediate sign-in build.** The file held the device's private keys and account refresh material in plaintext, sometimes world-readable; upgrading removes it. Only the exact legacy shape is removed. ([#10697](https://github.com/code-yeongyu/oh-my-openagent/issues/10697), [#10698](https://github.com/code-yeongyu/oh-my-openagent/pull/10698))
|
|
45
|
+
|
|
46
|
+
**Report a problem from inside OmO ([#10546](https://github.com/code-yeongyu/oh-my-openagent/pull/10546)).** `/feedback` (or `/bug`) sends a bug report or an idea to the OmO team in one step: your description plus your account, this device, versions, this conversation's recent messages, recent logs and your settings. Passwords, keys and tokens are removed first with the same redactor `/share` uses, your memory is never sent, and `OMO_DISABLE_FEEDBACK=1` saves the report as a local file instead of uploading it. The desktop app's Report a problem dialog uses the same report.
|
|
47
|
+
|
|
48
|
+
**A DAG node whose child dies on infrastructure is retried instead of taking its whole branch down.** When a mass-ulw node's child stops answering its heartbeats or loses its host session, the node is now retried automatically, at most twice, with a backoff. Its dependents are skipped only if the retries fail too. Before, one starved wave failed and every later node was skipped (an overloaded machine skipped 54 of 61 nodes). Task failures and cancelled nodes behave as before, and the TUI and the desktop app share this behaviour ([#10605](https://github.com/code-yeongyu/oh-my-openagent/issues/10605), [#10616](https://github.com/code-yeongyu/oh-my-openagent/pull/10616)).
|
|
49
|
+
|
|
50
|
+
**Stopping a language server on Windows no longer raises a stray broken-pipe error.** When an LSP server's pipe closed while OmO was shutting the server down, Windows could report the failed write after cleanup had already let go of it, and the EPIPE escaped as an unhandled error. Every pipe of a spawned language server now keeps an error handler for its whole life, so the failure is logged as ignored cleanup noise ([#9601](https://github.com/code-yeongyu/oh-my-openagent/issues/9601), [#10725](https://github.com/code-yeongyu/oh-my-openagent/pull/10725)).
|
|
51
|
+
|
|
52
|
+
**The frontend skill now covers HTML reports, figures and slides, and its stripe check catches square callouts.** The skill now loads for any HTML, CSS or SVG made for visual output (a report printed to PDF, a figure, a diagram, a slide, an image for Notion or Slack), not only web UI. Its `check-accent-stripes.ts` linter fails a coloured bar along one side of a box whether the box is rounded or square, and a 2px-or-wider accent outline on a rounded card. Neutral hairlines, table rules, a blockquote's neutral rule, focus rings and input validation states stay allowed. The `visualize` skill runs the linter on every page before previewing or showing it ([#10774](https://github.com/code-yeongyu/oh-my-openagent/issues/10774), [#10776](https://github.com/code-yeongyu/oh-my-openagent/pull/10776)).
|
|
53
|
+
|
|
54
|
+
**`ulw-research` shows its report inline and checks a visual reproduction against the source ([#10812](https://github.com/code-yeongyu/oh-my-openagent/pull/10812)).** When the host can show an HTML page in the thread (`show_html_page`, `html_render`), the run ends with the report there, before the file paths, on every tier and even when the research was shortened; a built app or 3D scene shown inline no longer stands in for it. When the ask is to recreate something visual, such as a clip rebuilt in 3D, the run puts real frames of the source next to its own output at the same moments, writes down what differs, fixes the biggest gaps, and shows the side-by-side pairs in the report.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Combine macOS root-test shards and Codex/Senpi compatibility into one runner job, preserving their existing check names through Ubuntu status jobs. Full-matrix CI now requests two macOS runners per head instead of five, including unchanged native binary parity; fast paths skip the combined allocation ([#10804](https://github.com/code-yeongyu/oh-my-openagent/pull/10804)).
|
|
60
|
+
|
|
61
|
+
- CI: pull requests run the macOS and Windows legs only when a platform-specific path changes: any path a macOS/Windows-only CI step names (derived from the workflow), native crates, the launcher and platform packaging, the Windows Git Bash runtime, the Codex installer, platform-named sources, and the CI workflows. Pushes to `dev` and merge groups still run every OS, and the `run-full-ci` label forces the full matrix on a pull request ([#10856](https://github.com/code-yeongyu/oh-my-openagent/issues/10856)). ([#10859](https://github.com/code-yeongyu/oh-my-openagent/pull/10859))
|
|
62
|
+
|
|
10
63
|
## [5.1.40] - 2026-10-20
|
|
11
64
|
|
|
12
65
|
**Onboarding saves only the facts you keep, and the TUI and the desktop share one completion flag.** The facts you accept are written through memory's own write path, and finishing onboarding in either the TUI or the desktop marks it done for both. ([#10506](https://github.com/code-yeongyu/oh-my-openagent/pull/10506))
|
|
@@ -32,7 +32,7 @@ Work with bounded reads. Determine file size first with `wc -c`; read small file
|
|
|
32
32
|
The primary agent's context (its prompts, skills, and external memory files) is stored in a memory filesystem rooted at `$MEMORY_DIR`. Changes to these files reach the primary agent's context after they're committed to the memory git repo.
|
|
33
33
|
|
|
34
34
|
The filesystem contains:
|
|
35
|
-
- **Prompts** (`system/`): always in-context. Reserve for identity, preferences, conventions, and active project context the agent needs on every turn. Keep files concise; move verbose content to external memory. `system/boundaries.md` is the user's exact words about what the agent must not do: leave it as you found it. `system/self-aware.md` is yours: promote a line from `reference/self/observations.md` when a later outcome confirmed it, edit entries in place, keep at most 12, and move expired or disconfirmed ones to `reference/self/ARCHIVE.md`. Identity stays in persona; the user's words stay in boundaries.
|
|
35
|
+
- **Prompts** (`system/`): always in-context. Reserve for identity, preferences, conventions, and active project context the agent needs on every turn. Keep files concise; move verbose content to external memory. `system/boundaries.md` is the user's exact words about what the agent must not do: leave it as you found it. `system/agreements.md` is the counted agreement ledger; do not edit it (the run is rejected if you do), because its counts are kept by the facts lane and `/agreements`. `system/self-aware.md` is yours: promote a line from `reference/self/observations.md` when a later outcome confirmed it, edit entries in place, keep at most 12, and move expired or disconfirmed ones to `reference/self/ARCHIVE.md`. Identity stays in persona; the user's words stay in boundaries.
|
|
36
36
|
- **Skills** (`skills/`): procedural memory for specialized workflows.
|
|
37
37
|
- **External memory** (everything else): reference material retrieved on demand by name and description. This includes `notes/facts/<YYYY-MM>.md` fact files and `people/` observation ledgers.
|
|
38
38
|
|
|
@@ -52,7 +52,7 @@ Read `$AUDIT_PATH` and repair findings before consolidation:
|
|
|
52
52
|
- `path_orphan`: use `git mv` into the home its content belongs to.
|
|
53
53
|
- `frontmatter_invalid`: leave to the writer owning the file unless only the description is missing.
|
|
54
54
|
|
|
55
|
-
Never touch `system/boundaries.md` or delete evidence. Report unresolved findings with reasons.
|
|
55
|
+
Never touch `system/boundaries.md` or `system/agreements.md`, and never delete evidence. Report unresolved findings with reasons.
|
|
56
56
|
|
|
57
57
|
## Phase 2: Consolidate
|
|
58
58
|
|
|
@@ -13,4 +13,15 @@ Use `scope: "person"` only for an explicit proper name, a known alias from the p
|
|
|
13
13
|
|
|
14
14
|
Use absolute dates. Resolve relative dates against `today` in the payload. Omit ephemera, guesses, plans not adopted, transient task state, and facts already contradicted in the same transcript. Keep each record self-contained and preserve the stated meaning without adding inference.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
## Agreements
|
|
17
|
+
|
|
18
|
+
When the payload has `agreements` and the `record_agreement` tool is available, treat every correction, prohibition, or standing instruction from the person about how the agent must work as an agreement, not a fact. Cues include "again", "I told you", "never", "stop doing", "how many times", 자꾸, 또, 다시는, 몇 번 말해야.
|
|
19
|
+
|
|
20
|
+
- If an item in `agreements` already requires what the person is correcting, call `record_agreement` with `{"action":"reaffirm","id":"<that item's id>","date":"YYYY-MM-DD"}`. Add `"text"` with a stronger wording only when that item is `by: "agent"`; never reword a `by: "human"` item.
|
|
21
|
+
- Otherwise call it with `{"action":"new","applies":"global"|"project","text":"...","date":"YYYY-MM-DD"}`. Use `project` when the rule is about this repository only. Write the text as one imperative rule in the person's language, at most 240 characters.
|
|
22
|
+
- One call per correction event. A correction repeated twice in the payload is two reaffirms. Never invent an id.
|
|
23
|
+
- Only the person's own words in user turns count. An agent's suggestion, plan, or recommendation is never an agreement, even when the person did not object to it.
|
|
24
|
+
|
|
25
|
+
Facts about how to treat the person stay `record_fact` person records; rules about how the agent must work go to `record_agreement`.
|
|
26
|
+
|
|
27
|
+
Call nothing when no durable fact or agreement qualifies.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// omo:
|
|
1
|
+
// omo:E7JXJ6LzVrHaMKRkWNlHWq0HbBiFzPKOood_scJBn2M: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};
|