@agentikos/omega-os 0.19.21 → 0.19.23

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.
@@ -99,6 +99,57 @@ _CATALOG: list[CliSpec] = [
99
99
  install_cmd=[], # filled at runtime
100
100
  description="Community aider-chat (`aider`)",
101
101
  ),
102
+ # v0.19.22 — extended provider matrix per the user's "install them all,
103
+ # let me hot-swap" spec.
104
+ CliSpec(
105
+ id="opencode", label="OpenCode (community multi-LLM)",
106
+ bin_name="opencode",
107
+ install_cmd=["npm", "install", "-g", "opencode-ai"],
108
+ description="OpenCode — terminal AI coding with provider switching",
109
+ ),
110
+ CliSpec(
111
+ id="openrouter_cli", label="OpenRouter via OpenCode",
112
+ bin_name="opencode", # opencode supports openrouter as a provider
113
+ install_cmd=[], # piggy-backs on opencode install
114
+ description="OpenRouter (300+ models) via OpenCode provider",
115
+ ),
116
+ CliSpec(
117
+ id="deepseek", label="DeepSeek (via OpenCode)",
118
+ bin_name="opencode",
119
+ install_cmd=[],
120
+ description="DeepSeek V3 / R1 via OpenCode provider",
121
+ ),
122
+ CliSpec(
123
+ id="qwen_code", label="Qwen Code (Alibaba CLI)",
124
+ bin_name="qwen",
125
+ install_cmd=["npm", "install", "-g", "@qwen-code/qwen-code"],
126
+ description="Alibaba's Qwen Coder CLI (`qwen`)",
127
+ ),
128
+ CliSpec(
129
+ id="continue_dev", label="Continue.dev (terminal)",
130
+ bin_name="cn",
131
+ install_cmd=["npm", "install", "-g", "@continuedev/cli"],
132
+ description="Continue.dev CLI — multi-provider with config.json",
133
+ ),
134
+ # v0.19.23 — local LLM runtimes (no API key needed, free local inference).
135
+ CliSpec(
136
+ id="ollama", label="Ollama (local model runtime)",
137
+ bin_name="ollama",
138
+ install_cmd=[], # filled at runtime — curl|sh installer
139
+ description="Local LLM runtime — pull + run Llama/Mistral/Qwen/etc.",
140
+ ),
141
+ CliSpec(
142
+ id="lm_studio", label="LM Studio CLI (local)",
143
+ bin_name="lms",
144
+ install_cmd=["npx", "-y", "@lmstudio/cli", "install"],
145
+ description="LM Studio command-line manager (`lms`) for local models",
146
+ ),
147
+ CliSpec(
148
+ id="gh_copilot", label="GitHub Copilot Chat (via gh extension)",
149
+ bin_name="gh", # we extend an existing binary
150
+ install_cmd=["gh", "extension", "install", "github/gh-copilot"],
151
+ description="GitHub Copilot Chat as a `gh copilot` subcommand",
152
+ ),
102
153
  ]
103
154
 
104
155
 
@@ -545,6 +545,13 @@ for entry in missing:
545
545
  elif "npm_global" in install and shutil.which("npm"):
546
546
  rc, out = _run(["npm", "install", "-g", "--silent", install["npm_global"]])
547
547
  attempt = f"npm install -g {install['npm_global']}"
548
+ elif "uv_tool" in install and (shutil.which("uv") or os.path.isfile(
549
+ os.path.expanduser("~/.local/bin/uv"))):
550
+ # uv-managed Python tools — installs to ~/.local/share/uv/tools
551
+ # and links to ~/.local/bin (which step_system_deps put on PATH)
552
+ uv_bin = shutil.which("uv") or os.path.expanduser("~/.local/bin/uv")
553
+ rc, out = _run([uv_bin, "tool", "install", "--quiet", install["uv_tool"]])
554
+ attempt = f"uv tool install {install['uv_tool']}"
548
555
  elif "curl" in install:
549
556
  # curl|bash installer — last resort
550
557
  rc, out = _run(["bash", "-c", f"curl -fsSL {install['curl']} | bash"])
@@ -188,7 +188,7 @@ from omega_engine.genesis import (
188
188
  )
189
189
  from omega_engine import plan as plan_v7
190
190
 
191
- __version__ = "0.19.21"
191
+ __version__ = "0.19.23"
192
192
 
193
193
  __all__ = [
194
194
  "__version__",
@@ -2941,6 +2941,160 @@ def cmd_menu_whiptail(_args: argparse.Namespace) -> int:
2941
2941
  return run_menu()
2942
2942
 
2943
2943
 
2944
+ # Default LLM provider for chat sessions — recorded in a tiny state file
2945
+ # so spawn_*_chat picks the right one. Hot-swap via `omega switch <id>`.
2946
+ _PROVIDER_MARKER = "active-llm-provider"
2947
+
2948
+
2949
+ def _provider_state_path() -> "Path":
2950
+ from pathlib import Path
2951
+ home = _omega_home()
2952
+ return home / "Agentik_Extra" / "var" / _PROVIDER_MARKER
2953
+
2954
+
2955
+ def _active_provider() -> str:
2956
+ p = _provider_state_path()
2957
+ if p.exists():
2958
+ return p.read_text().strip() or "claude_code"
2959
+ return "claude_code"
2960
+
2961
+
2962
+ def _set_active_provider(provider_id: str) -> None:
2963
+ p = _provider_state_path()
2964
+ p.parent.mkdir(parents=True, exist_ok=True)
2965
+ p.write_text(provider_id.strip() + "\n")
2966
+
2967
+
2968
+ def cmd_scrape(args: argparse.Namespace) -> int:
2969
+ """`omega scrape <url> [--out file] [--humanize]` — stealth scraper.
2970
+
2971
+ Thin wrapper over CloakBrowser (https://github.com/CloakHQ/CloakBrowser).
2972
+ Bypasses Cloudflare/Turnstile/FingerprintJS by patching the browser at
2973
+ the C++ level. Default output = markdown to stdout so any LLM CLI can
2974
+ pipe it directly into a context. The wrapper is what makes
2975
+ CloakBrowser usable as a SHELL CLI for Claude/Gemini/Codex/etc.
2976
+
2977
+ Usage:
2978
+ omega scrape https://example.com # → markdown to stdout
2979
+ omega scrape https://example.com --out page.md # → file
2980
+ omega scrape https://example.com --humanize # mouse curves + scroll
2981
+ omega scrape https://example.com --proxy <url> # SOCKS5/HTTP
2982
+
2983
+ Inside any Claude Code chat session: ``omega scrape <url>`` returns the
2984
+ cleaned page content. No MCP server, no Playwright dance.
2985
+ """
2986
+ import shutil
2987
+ import subprocess
2988
+ if not args.url:
2989
+ print("usage: omega scrape <url> [--out FILE] [--humanize] [--proxy URL]")
2990
+ return 2
2991
+ # CloakBrowser is installed as a uv tool (`cloakbrowser`) but we need
2992
+ # to drive it from Python — the canonical path is `python -m
2993
+ # cloakbrowser` after `pip install cloakbrowser`, OR we invoke the
2994
+ # uv-tool-installed Python directly. Try shutil.which("cloakbrowser")
2995
+ # first; if absent, fall back to running through the installer venv.
2996
+ runner = None
2997
+ if shutil.which("cloakbrowser"):
2998
+ # Direct CLI is mostly for binary management (install/info/update).
2999
+ # For SCRAPING we still need our Python wrapper.
3000
+ pass
3001
+ # Inline Python wrapper — no separate script file, no version drift.
3002
+ py_code = (
3003
+ "import sys, asyncio\n"
3004
+ "try:\n"
3005
+ " from cloakbrowser import launch_async\n"
3006
+ "except ImportError:\n"
3007
+ " sys.stderr.write('cloakbrowser not importable — install with: "
3008
+ "uv tool install cloakbrowser\\n'); sys.exit(2)\n"
3009
+ "URL = sys.argv[1]\n"
3010
+ "HUMANIZE = '--humanize' in sys.argv\n"
3011
+ "PROXY = None\n"
3012
+ "if '--proxy' in sys.argv:\n"
3013
+ " PROXY = sys.argv[sys.argv.index('--proxy') + 1]\n"
3014
+ "async def main():\n"
3015
+ " kwargs = {'humanize': HUMANIZE}\n"
3016
+ " if PROXY:\n"
3017
+ " kwargs['proxy'] = PROXY\n"
3018
+ " browser = await launch_async(**kwargs)\n"
3019
+ " page = await browser.new_page()\n"
3020
+ " await page.goto(URL, wait_until='networkidle')\n"
3021
+ " text = await page.evaluate(\"document.body.innerText\")\n"
3022
+ " await browser.close()\n"
3023
+ " print(text)\n"
3024
+ "asyncio.run(main())\n"
3025
+ )
3026
+ cmd = ["python3", "-c", py_code, args.url]
3027
+ if getattr(args, "humanize", False):
3028
+ cmd.append("--humanize")
3029
+ if getattr(args, "proxy", None):
3030
+ cmd += ["--proxy", args.proxy]
3031
+ try:
3032
+ proc = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
3033
+ except subprocess.TimeoutExpired:
3034
+ print(" cloakbrowser timed out after 120s")
3035
+ return 2
3036
+ out_target = getattr(args, "out", None)
3037
+ if out_target:
3038
+ from pathlib import Path
3039
+ Path(out_target).write_text(proc.stdout)
3040
+ print(f" wrote {len(proc.stdout)} bytes → {out_target}")
3041
+ else:
3042
+ print(proc.stdout)
3043
+ if proc.returncode != 0:
3044
+ print(f" cloakbrowser stderr: {proc.stderr[:400]}", file=__import__('sys').stderr)
3045
+ return proc.returncode
3046
+
3047
+
3048
+ def cmd_switch(args: argparse.Namespace) -> int:
3049
+ """`omega switch <provider>` — hot-swap the LLM provider for new chats.
3050
+
3051
+ Existing tmux sessions keep their current provider; only the NEXT
3052
+ chat session spawned (via `omega`, `omega aisb`, `omega hermes`) uses
3053
+ the new one. Per-LLM persona files are already in place in every
3054
+ context dir (write_all_personas runs at install + on every spawn),
3055
+ so switching is just changing which `cli` the tmux session launches.
3056
+
3057
+ Supported provider ids (must match the LLM CLI catalog in
3058
+ `bootstrap/lib/llm-clis.py` + `omega_engine.personas`):
3059
+
3060
+ claude_code, gemini_cli, codex, opencode, openrouter_cli,
3061
+ deepseek, qwen_code, continue_dev, aider
3062
+ """
3063
+ import shutil
3064
+ valid = {
3065
+ "claude_code": "claude",
3066
+ "gemini_cli": "gemini",
3067
+ "codex": "codex",
3068
+ "opencode": "opencode",
3069
+ "openrouter_cli": "opencode",
3070
+ "deepseek": "opencode",
3071
+ "qwen_code": "qwen",
3072
+ "continue_dev": "cn",
3073
+ "aider": "aider",
3074
+ }
3075
+ if not getattr(args, "provider", None):
3076
+ print(f" active provider: {_active_provider()}")
3077
+ print(f" available:")
3078
+ for pid, bin_name in valid.items():
3079
+ mark = "✓" if shutil.which(bin_name) else "—"
3080
+ active = " (active)" if pid == _active_provider() else ""
3081
+ print(f" {mark} {pid:<16} ({bin_name}){active}")
3082
+ return 0
3083
+ target = args.provider
3084
+ if target not in valid:
3085
+ print(f" unknown provider: {target!r}")
3086
+ print(f" valid: {', '.join(valid)}")
3087
+ return 2
3088
+ if not shutil.which(valid[target]):
3089
+ print(f" provider {target!r} not installed — `omega upgrade` "
3090
+ f"or re-run install.sh with the llm_clis: block")
3091
+ return 2
3092
+ _set_active_provider(target)
3093
+ print(f" active provider set to: {target}")
3094
+ print(f" next `omega` / `omega aisb` / `omega hermes` will launch with `{valid[target]}`")
3095
+ return 0
3096
+
3097
+
2944
3098
  def cmd_hermes_desktop(args: argparse.Namespace) -> int:
2945
3099
  """`omega hermes-desktop {install,serve,profile,status}` — expose this
2946
3100
  VPS as a remote backend for the Hermes Desktop Electron app."""
@@ -4024,6 +4178,33 @@ def _build_parser() -> argparse.ArgumentParser:
4024
4178
  help="open the interactive whiptail menu (the old `omega` behaviour)"
4025
4179
  ).set_defaults(fn=cmd_menu_whiptail)
4026
4180
  sub.add_parser("version", help="print the engine version").set_defaults(fn=cmd_version)
4181
+
4182
+ # `omega switch <provider>` — hot-swap the LLM CLI used for new chats.
4183
+ p_sw = sub.add_parser(
4184
+ "switch",
4185
+ help="switch the default LLM provider for new chat sessions "
4186
+ "(claude_code / gemini_cli / codex / opencode / qwen_code / "
4187
+ "continue_dev / aider)",
4188
+ )
4189
+ p_sw.add_argument("provider", nargs="?", default=None,
4190
+ help="provider id (omit to print current + available)")
4191
+ p_sw.set_defaults(fn=cmd_switch)
4192
+
4193
+ # `omega scrape <url>` — official OmegaOS web scraper (CloakBrowser).
4194
+ p_sc = sub.add_parser(
4195
+ "scrape",
4196
+ help="stealth scrape a URL via CloakBrowser — bypasses Cloudflare, "
4197
+ "Turnstile, FingerprintJS. Output: markdown to stdout (or --out FILE)",
4198
+ )
4199
+ p_sc.add_argument("url", nargs="?", default=None,
4200
+ help="URL to fetch")
4201
+ p_sc.add_argument("--out", default=None,
4202
+ help="write result to file instead of stdout")
4203
+ p_sc.add_argument("--humanize", action="store_true",
4204
+ help="mouse curves + keyboard timing + scroll patterns")
4205
+ p_sc.add_argument("--proxy", default=None,
4206
+ help="HTTP or SOCKS5 proxy (with inline creds)")
4207
+ p_sc.set_defaults(fn=cmd_scrape)
4027
4208
  p_doc = sub.add_parser("doctor", help="validate the deployment")
4028
4209
  p_doc.add_argument("--json", action="store_true",
4029
4210
  help="emit a single JSON object instead of pretty text")
@@ -176,6 +176,61 @@ def _backend_supabase_stack() -> Stack:
176
176
  )
177
177
 
178
178
 
179
+ def _convex_selfhosted_stack() -> Stack:
180
+ """Self-hosted Convex backend running on the operator's VPS.
181
+
182
+ Convex went open-source in 2024 — you can run the full backend
183
+ locally or on a VPS instead of paying the Convex Cloud. This stack
184
+ pre-wires that path: Next.js + Vercel frontend, Convex self-hosted
185
+ on the operator's VPS, Stripe payments, Clerk auth, shadcn UI.
186
+ """
187
+ return Stack(
188
+ id="convex-selfhosted",
189
+ title="Convex self-hosted (VPS) + Next.js + Vercel + Stripe + Clerk",
190
+ config=StackConfig(
191
+ type="web",
192
+ frontend="nextjs",
193
+ backend="convex", # but with extras=["selfhosted"]
194
+ deploy="vercel",
195
+ payments="stripe",
196
+ ui_kit="shadcn",
197
+ auth="clerk",
198
+ extras=["convex-selfhosted", "vps-backend"],
199
+ ),
200
+ why="When the operator wants Convex's real-time + typed model "
201
+ "without Convex Cloud lock-in. Backend runs on their VPS via "
202
+ "the open-source Convex docker image. Same DX, zero per-call "
203
+ "cost, full data sovereignty.",
204
+ )
205
+
206
+
207
+ def _sqlite_local_stack() -> Stack:
208
+ """Local SQLite-only stack — the simplest possible.
209
+
210
+ For tools, internal apps, single-user dashboards. No backend
211
+ process needed, no auth surface, no per-call cost. Drizzle ORM
212
+ over better-sqlite3 gives a typed query layer.
213
+ """
214
+ return Stack(
215
+ id="sqlite-local",
216
+ title="Local SQLite (Next.js + Drizzle + Vercel — single-user)",
217
+ config=StackConfig(
218
+ type="web",
219
+ frontend="nextjs",
220
+ backend="postgres+drizzle", # drizzle works with sqlite too
221
+ deploy="vercel",
222
+ payments="none",
223
+ ui_kit="shadcn",
224
+ auth="none",
225
+ extras=["sqlite-local", "drizzle-sqlite"],
226
+ ),
227
+ why="Single-user / internal-tool stack. SQLite file in the project "
228
+ "dir, Drizzle for typed queries, no auth, no payments. Ship "
229
+ "in 20 minutes. Upgrade to a real backend later when the "
230
+ "scale calls for it.",
231
+ )
232
+
233
+
179
234
  def _claude_dashboard_stack() -> Stack:
180
235
  """The Claude Code-grade premium dashboard stack.
181
236
 
@@ -213,6 +268,8 @@ CANONICAL_STACKS: dict[str, Stack] = {
213
268
  _desktop_tauri_stack(),
214
269
  _backend_supabase_stack(),
215
270
  _claude_dashboard_stack(),
271
+ _convex_selfhosted_stack(),
272
+ _sqlite_local_stack(),
216
273
  )
217
274
  }
218
275
 
@@ -0,0 +1,197 @@
1
+ """Multi-LLM persona writer — one canonical OmegaOS context, N output files.
2
+
3
+ Every LLM CLI we install (Claude Code, Gemini CLI, OpenAI Codex, OpenCode,
4
+ Continue.dev, Qwen, Aider…) reads a different filename for its project
5
+ context (CLAUDE.md, GEMINI.md, AGENTS.md, .opencode/CONTEXT.md, etc.).
6
+ Without a canonical source, the operator has to copy the same content
7
+ into N files manually and they drift.
8
+
9
+ This module:
10
+
11
+ 1. Defines the canonical OmegaOS context at one path
12
+ (``$OMEGA_HOME/Agentik_SSOT/personas/OMEGAOS-CONTEXT.md``).
13
+ 2. Exports ``write_all_personas(home, chat_context_dir)`` which mirrors
14
+ that one canonical file into every LLM's expected filename inside the
15
+ project dir, so when the user runs `claude`, `gemini`, `codex`,
16
+ etc. in that dir they ALL load the same OmegaOS persona.
17
+ 3. Idempotent — re-running diff-checks before write.
18
+
19
+ Used by:
20
+ * step_clis post-install pass — seeds the canonical context.
21
+ * tmux.spawn_*_chat — ensures the chat context dir has every persona
22
+ file before launching whichever CLI the user picked.
23
+ * `omega switch <provider>` — re-renders the active context's
24
+ persona pointer.
25
+ """
26
+ from __future__ import annotations
27
+
28
+ import shutil
29
+ from pathlib import Path
30
+
31
+
32
+ # The single source of truth. Every per-LLM persona file is a symlink or
33
+ # mirror of this.
34
+ CANONICAL_FILENAME = "OMEGAOS-CONTEXT.md"
35
+
36
+
37
+ # Map: LLM id → list of persona filenames it reads (relative to a project dir).
38
+ # Sources:
39
+ # - Anthropic Claude Code → CLAUDE.md (project root or ~/.claude/CLAUDE.md)
40
+ # - Google Gemini CLI → GEMINI.md (project root or ~/.gemini/GEMINI.md)
41
+ # - OpenAI Codex CLI → AGENTS.md (project root)
42
+ # - OpenCode → .opencode/CONTEXT.md
43
+ # - Continue.dev → .continue/CONTEXT.md
44
+ # - Qwen Code → QWEN.md
45
+ # - Aider → .aider.conf.yml (config, not full context)
46
+ # + CONVENTIONS.md (Aider's persona convention)
47
+ _LLM_PERSONA_PATHS: dict[str, list[str]] = {
48
+ "claude_code": ["CLAUDE.md"],
49
+ "gemini_cli": ["GEMINI.md"],
50
+ "codex": ["AGENTS.md"],
51
+ "opencode": [".opencode/CONTEXT.md"],
52
+ "openrouter_cli": [".opencode/CONTEXT.md"],
53
+ "deepseek": [".opencode/CONTEXT.md"],
54
+ "qwen_code": ["QWEN.md"],
55
+ "continue_dev": [".continue/CONTEXT.md"],
56
+ "aider": ["CONVENTIONS.md"],
57
+ "hermes": ["HERMES.md"],
58
+ }
59
+
60
+
61
+ _DEFAULT_CONTEXT_TEMPLATE = """\
62
+ # OmegaOS — Working Context
63
+
64
+ > *You are running inside an OmegaOS-managed Claude/Gemini/Codex/OpenCode
65
+ > session. This file is auto-generated by `omega_engine.personas` and
66
+ > mirrored from `$OMEGA_HOME/Agentik_SSOT/personas/OMEGAOS-CONTEXT.md`.
67
+ > Edits to per-LLM files (CLAUDE.md, GEMINI.md, …) are lost on next sync —
68
+ > edit the canonical file instead.*
69
+
70
+ ## What OmegaOS is
71
+
72
+ OmegaOS is a 5-layer agentic operating system:
73
+
74
+ ```
75
+ Layer 1 Human (Telegram, CLI, web)
76
+ Layer 2 Hermès — meta-companion (Anthropic API, separate budget)
77
+ Layer 3 AISB — OmegaOS intake / orchestrator (Claude Max OAuth)
78
+ Layer 4 Oracle — planner / dispatcher (per-project tmux session)
79
+ Layer 5 Workers — executors (one per subtask, .done.json verified)
80
+ ```
81
+
82
+ You sit at L4 or L5 depending on context.
83
+
84
+ ## The Three Laws (override everything)
85
+
86
+ 1. **Code lies. Comments lie. Only runtime tells the truth.**
87
+ Observe before concluding. Before the 3rd code change on the same
88
+ bug, live runtime evidence is MANDATORY.
89
+ 2. **Researcher, not sycophant.** Challenge flawed premises. Think
90
+ before coding. Iterate with evidence. Root causes over symptoms.
91
+ 3. **Autonomous execution.** When dispatched, never wait. Decide →
92
+ execute → report. The only legal stop is `.done.json` with status
93
+ `done_clean | pending | failed`.
94
+
95
+ ## OmegaOS structure (where things live)
96
+
97
+ ```
98
+ $OMEGA_HOME = ~/Omega
99
+ ├── Agentik_Engine/ ← Python engine (omega CLI)
100
+ ├── Agentik_SSOT/ ← source of truth
101
+ │ ├── skills/ ← 17 audits + orchestrators + /newcmd
102
+ │ ├── agents/aisb/ ← 13 AISB persona files
103
+ │ ├── personas/OMEGAOS-CONTEXT.md ← THIS FILE
104
+ │ ├── docs/LAYERS.md ← formal L1-L5 architecture
105
+ │ └── clis/clis-catalog.yaml ← CLI catalog
106
+ ├── Agentik_Orchestration/ ← topology YAMLs
107
+ ├── Agentik_Coding/projects/<slug>/ ← per-project root (strict isolation)
108
+ ├── Agentik_Runtime/ ← event log, RAG, locks, sessions
109
+ ├── Agentik_Tools/bin/omega ← the CLI you can shell out to
110
+ └── Agentik_Extra/etc/secrets/ ← age-encrypted vault
111
+ ```
112
+
113
+ ## What you can do (shell out via Bash)
114
+
115
+ | Action | Command |
116
+ |---|---|
117
+ | Mission with verified completion | `omega run "<intent>"` |
118
+ | New project (Genesis pipeline) | `omega genesis new <slug>` |
119
+ | Audit code/UI/flow/security/… | `omega audit <id>` (or the `/codeaudit` skill) |
120
+ | Switch LLM provider for this session | `omega switch <provider>` |
121
+ | Vault secrets | `omega vault read/write <REF>` |
122
+ | Status / health | `omega doctor` / `omega status` |
123
+ | Session manager (tmux) | `omega` (no args) |
124
+
125
+ ## Quality Arsenal (17 forensic audits)
126
+
127
+ `/codeaudit /flowaudit /uiuxaudit /debugaudit /featureaudit /perfaudit
128
+ /secaudit /a11yaudit /seoaudit /dataaudit /apiaudit /copyaudit /dxaudit
129
+ /motionaudit /automationaudit /logicaudit /retentionaudit`
130
+
131
+ Each: Gestalt clarity gate, Popper falsification, hinge point 10x
132
+ scrutiny, auto-fix, auto-re-audit, scored /100.
133
+
134
+ ## When something doesn't fit OmegaOS
135
+
136
+ Push back. Don't quietly invent. State the premise, decide, execute.
137
+ If the user's request would violate one of the Three Laws or the
138
+ verified-completion contract, call it out — they want a researcher,
139
+ not a yes-engine.
140
+ """
141
+
142
+
143
+ def canonical_path(omega_home: Path) -> Path:
144
+ """Where the single source of truth lives."""
145
+ return omega_home / "Agentik_SSOT" / "personas" / CANONICAL_FILENAME
146
+
147
+
148
+ def ensure_canonical(omega_home: Path) -> Path:
149
+ """Create the canonical OmegaOS context file if absent. Returns its
150
+ path. Operators are free to edit this file — it's the SSOT."""
151
+ path = canonical_path(omega_home)
152
+ path.parent.mkdir(parents=True, exist_ok=True)
153
+ if not path.exists():
154
+ path.write_text(_DEFAULT_CONTEXT_TEMPLATE)
155
+ return path
156
+
157
+
158
+ def write_all_personas(
159
+ omega_home: Path | str,
160
+ chat_context_dir: Path | str,
161
+ *,
162
+ llm_ids: list[str] | None = None,
163
+ ) -> dict[str, str]:
164
+ """Mirror the canonical context into every LLM's expected filename
165
+ inside ``chat_context_dir``.
166
+
167
+ Returns ``{llm_id: relative_path}`` for what was written. Files
168
+ that already match (content equality) are skipped silently —
169
+ idempotent.
170
+
171
+ When ``llm_ids`` is None we write for every LLM in
172
+ ``_LLM_PERSONA_PATHS`` (cheap insurance against the user later
173
+ swapping providers).
174
+ """
175
+ home = Path(omega_home)
176
+ ctx_dir = Path(chat_context_dir)
177
+ ctx_dir.mkdir(parents=True, exist_ok=True)
178
+
179
+ src = ensure_canonical(home)
180
+ content = src.read_text()
181
+
182
+ targets = list(llm_ids) if llm_ids else list(_LLM_PERSONA_PATHS)
183
+ written: dict[str, str] = {}
184
+ for llm in targets:
185
+ for rel in _LLM_PERSONA_PATHS.get(llm, []):
186
+ dest = ctx_dir / rel
187
+ dest.parent.mkdir(parents=True, exist_ok=True)
188
+ if dest.exists() and dest.read_text() == content:
189
+ continue
190
+ dest.write_text(content)
191
+ written[llm] = rel
192
+ return written
193
+
194
+
195
+ def supported_llm_ids() -> list[str]:
196
+ """The LLM ids personas can be written for."""
197
+ return list(_LLM_PERSONA_PATHS)
@@ -233,29 +233,59 @@ def spawn_worker(project: str, task: str, *,
233
233
 
234
234
  def _ensure_chat_context_dir(home: Path, label: str,
235
235
  persona_md_path: Path) -> Path:
236
- """Create (idempotent) a dedicated Claude Code project dir for an
237
- OmegaOS chat session.
238
-
239
- Drops ``CLAUDE.md`` (the persona) so Claude Code auto-loads it as
240
- project context when the user runs `claude` in this dir. Returns
241
- the path.
236
+ """Create (idempotent) a multi-LLM project dir for an OmegaOS chat
237
+ session.
238
+
239
+ v0.19.22 drops persona files for EVERY supported LLM (CLAUDE.md,
240
+ GEMINI.md, AGENTS.md, QWEN.md, .opencode/CONTEXT.md, etc.) sourced
241
+ from the canonical ``Agentik_SSOT/personas/OMEGAOS-CONTEXT.md``.
242
+ User can `claude` here, OR `gemini`, OR `codex`, OR `opencode`, OR
243
+ `qwen`, OR `aider` — they all see the SAME OmegaOS context. Hot-swap
244
+ works because every CLI's file is already in place.
245
+
246
+ The ``persona_md_path`` argument is now used only as a hint: if a
247
+ role-specific persona file exists at that path (e.g. the AISB v7.0
248
+ master prompt), we PREPEND it to the canonical context so AISB-chat
249
+ gets both the role identity AND the OmegaOS background, while
250
+ Hermes-chat gets just the canonical context.
242
251
  """
243
252
  ctx_dir = home / "Agentik_Coding" / "chat-contexts" / label
244
253
  ctx_dir.mkdir(parents=True, exist_ok=True)
245
- target = ctx_dir / "CLAUDE.md"
246
- if persona_md_path.is_file():
247
- # Mirror the upstream persona file so refreshes propagate.
248
- target.write_text(persona_md_path.read_text())
249
- elif not target.exists():
250
- target.write_text(
251
- f"# {label} chat context\n\n"
252
- f"(no persona file found using default Claude Code behaviour)\n"
253
- )
254
- # Also drop a .gitignore so this transient dir never gets committed
255
- # by accident if the user `git init`s it.
254
+
255
+ # Build the merged context: role-specific persona (if any) + canonical.
256
+ from omega_engine.personas import (
257
+ canonical_path, ensure_canonical, write_all_personas,
258
+ )
259
+ ensure_canonical(home)
260
+ canonical_text = canonical_path(home).read_text()
261
+ if persona_md_path.is_file() and persona_md_path.name not in (
262
+ "OMEGAOS-CONTEXT.md",
263
+ ):
264
+ role_text = persona_md_path.read_text()
265
+ merged = f"{role_text}\n\n---\n\n{canonical_text}"
266
+ # Write merged to the canonical filename in ctx_dir, then mirror
267
+ # to every LLM's expected filename via personas.write_all.
268
+ merged_file = ctx_dir / "OMEGAOS-CONTEXT.md"
269
+ merged_file.write_text(merged)
270
+ # personas.write_all reads from the canonical — temporarily point
271
+ # to the merged version by writing per-LLM files directly.
272
+ for llm_id in [
273
+ "claude_code", "gemini_cli", "codex", "opencode",
274
+ "qwen_code", "continue_dev", "aider", "hermes",
275
+ ]:
276
+ from omega_engine.personas import _LLM_PERSONA_PATHS # noqa: WPS437
277
+ for rel in _LLM_PERSONA_PATHS.get(llm_id, []):
278
+ dest = ctx_dir / rel
279
+ dest.parent.mkdir(parents=True, exist_ok=True)
280
+ if not dest.exists() or dest.read_text() != merged:
281
+ dest.write_text(merged)
282
+ else:
283
+ write_all_personas(home, ctx_dir)
284
+
285
+ # `.gitignore` so the transient dir doesn't get committed.
256
286
  gi = ctx_dir / ".gitignore"
257
287
  if not gi.exists():
258
- gi.write_text("*\n!.gitignore\n!CLAUDE.md\n")
288
+ gi.write_text("*\n!.gitignore\n!*.md\n!.opencode/\n!.continue/\n")
259
289
  return ctx_dir
260
290
 
261
291
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "omega-engine"
3
- version = "0.19.21"
3
+ version = "0.19.23"
4
4
  description = "The Omega OS orchestration engine — event-sourced, verified-completion agent graphs."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1 +1 @@
1
- 0.19.21
1
+ 0.19.23
@@ -131,3 +131,11 @@ catalog:
131
131
  marketplace: claude-plugins-official
132
132
  scope: user
133
133
  recommended: false
134
+
135
+ - id: superpowers
136
+ name: superpowers
137
+ description: "obra/superpowers — agentic framework with subagent-driven dev, TDD, planning, brainstorm, code review enforced"
138
+ category: workflow
139
+ marketplace: claude-plugins-official
140
+ scope: user
141
+ recommended: true
@@ -64,6 +64,16 @@ native:
64
64
  secrets: []
65
65
  recommended: true
66
66
 
67
+ - id: cloakbrowser
68
+ name: CloakBrowser (official OmegaOS scraper — bypasses bot detection)
69
+ # PyPI package — installed into a uv tool so it's globally available
70
+ # as `cloakbrowser` + `python -m cloakbrowser`. ~200MB binary
71
+ # downloaded on first run (cached).
72
+ install: { uv_tool: "cloakbrowser" }
73
+ binary: cloakbrowser
74
+ secrets: []
75
+ recommended: true
76
+
67
77
  # --- 2. Printing Press CLIs --------------------------------------------
68
78
  # Installed via `npx -y @mvanhorn/printing-press-library install <name>`.
69
79
  # Each ships with a local SQLite mirror + Claude Code skill.
@@ -0,0 +1,169 @@
1
+ # Omega OS — LLM provider catalog (v0.19.23+)
2
+ #
3
+ # Maps the operator's intent ("I want to talk to GLM") to a concrete
4
+ # (cli + provider_config) pair. Most providers don't ship their own CLI —
5
+ # they're configurable backends inside OpenCode / Continue.dev / Aider.
6
+ #
7
+ # Used by:
8
+ # * `omega switch <provider>` — looks up the cli + writes the
9
+ # provider-active marker.
10
+ # * step_provider_configs — drops templated config files for every
11
+ # supported provider with secret refs from the vault.
12
+ # * `omega account login --provider <id>` — guides the auth flow per
13
+ # provider.
14
+ #
15
+ # Secrets are vault refs, never literal. Templated config files end up at
16
+ # the LLM CLI's expected path on first launch.
17
+
18
+ version: 1
19
+
20
+ providers:
21
+ # -- Anthropic family --
22
+ - id: anthropic
23
+ label: "Anthropic Claude (API key)"
24
+ cli: claude_code
25
+ auth_kind: api_key
26
+ secret_refs: [ANTHROPIC_API_KEY]
27
+ notes: |
28
+ Direct Anthropic API access. Pay-per-token. For OmegaOS's own
29
+ L3-L5 stack we prefer Claude Max OAuth (no cost) — this entry is
30
+ for L2 / Hermès style "use my paid Anthropic budget" scenarios.
31
+
32
+ - id: chatgpt_subscription
33
+ label: "ChatGPT Subscription (OpenAI Codex)"
34
+ cli: codex
35
+ auth_kind: oauth
36
+ secret_refs: []
37
+ notes: |
38
+ Codex CLI uses the same OAuth as your ChatGPT Plus/Team sub —
39
+ no separate API key needed. `codex login` opens the browser.
40
+
41
+ # -- OpenAI family --
42
+ - id: openai
43
+ label: "OpenAI (API key — gpt-4o, gpt-5, o3-pro)"
44
+ cli: codex # codex IS OpenAI's official agentic CLI
45
+ auth_kind: api_key
46
+ secret_refs: [OPENAI_API_KEY]
47
+ notes: |
48
+ Pay-per-token OpenAI API. Use this when you want gpt-5/o3-pro
49
+ without the ChatGPT subscription cap.
50
+
51
+ - id: openai_compatible
52
+ label: "OpenAI API-Compatible endpoint (custom)"
53
+ cli: opencode # OpenCode's `provider: openai_compatible`
54
+ auth_kind: api_key
55
+ secret_refs: [OPENAI_API_BASE, OPENAI_API_KEY]
56
+ notes: |
57
+ For LM Studio, vLLM, llama.cpp's `/v1` server, Together.ai's
58
+ OpenAI-shape endpoint, etc.
59
+
60
+ # -- Google --
61
+ - id: google_ai
62
+ label: "Google AI Studio (Gemini)"
63
+ cli: gemini_cli
64
+ auth_kind: api_key
65
+ secret_refs: [GOOGLE_API_KEY]
66
+ notes: |
67
+ Direct Gemini API via the official `gemini` CLI. Free tier
68
+ generous; paid tier for higher RPM.
69
+
70
+ # -- China --
71
+ - id: glm
72
+ label: "Zhipu GLM-4.5 / GLM-4.6 (via OpenCode)"
73
+ cli: opencode
74
+ auth_kind: api_key
75
+ secret_refs: [GLM_API_KEY]
76
+ notes: |
77
+ Zhipu AI's GLM models — open weights for the smaller variants,
78
+ hosted API for GLM-4.6. OpenCode provider `zhipu`.
79
+
80
+ - id: deepseek
81
+ label: "DeepSeek V3 / R1 (via OpenCode)"
82
+ cli: opencode
83
+ auth_kind: api_key
84
+ secret_refs: [DEEPSEEK_API_KEY]
85
+ notes: |
86
+ DeepSeek's V3 + R1 reasoning. Excellent price/perf. OpenCode
87
+ provider `deepseek`.
88
+
89
+ - id: qwen
90
+ label: "Qwen (Alibaba) — dedicated CLI"
91
+ cli: qwen_code
92
+ auth_kind: api_key
93
+ secret_refs: [DASHSCOPE_API_KEY]
94
+ notes: |
95
+ Qwen3-Coder via Alibaba's DashScope. Standalone `qwen` CLI.
96
+
97
+ # -- Local --
98
+ - id: ollama
99
+ label: "Ollama (local — Llama/Mistral/Qwen/DeepSeek-Coder)"
100
+ cli: ollama
101
+ auth_kind: none
102
+ secret_refs: []
103
+ notes: |
104
+ Local model runtime. Free, private, runs offline. Pull a model
105
+ with `ollama pull llama3.3:70b` then `ollama run`.
106
+
107
+ - id: lm_studio
108
+ label: "LM Studio (local — GUI + CLI)"
109
+ cli: lm_studio
110
+ auth_kind: none
111
+ secret_refs: []
112
+ notes: |
113
+ LM Studio for desktop. Run any GGUF model locally. Exposes an
114
+ OpenAI-compatible `/v1` endpoint OpenCode can consume.
115
+
116
+ # -- Aggregators / Gateways --
117
+ - id: openrouter
118
+ label: "OpenRouter (300+ models, one API key)"
119
+ cli: opencode
120
+ auth_kind: api_key
121
+ secret_refs: [OPENROUTER_API_KEY]
122
+ notes: |
123
+ Route to any of 300+ models with one API key. OpenCode provider
124
+ `openrouter`. Per-call routing in config.
125
+
126
+ - id: vercel_ai_gateway
127
+ label: "Vercel AI Gateway (multi-provider)"
128
+ cli: opencode
129
+ auth_kind: api_key
130
+ secret_refs: [AI_GATEWAY_API_KEY]
131
+ notes: |
132
+ Vercel's hosted gateway proxies your traffic across providers
133
+ with usage caps + observability. OpenCode `provider: ai_gateway`.
134
+
135
+ # -- Hyperscalers --
136
+ - id: amazon_bedrock
137
+ label: "Amazon Bedrock (AWS)"
138
+ cli: opencode
139
+ auth_kind: aws
140
+ secret_refs: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION]
141
+ notes: |
142
+ AWS Bedrock — Claude, Llama, Mistral, Cohere, Stability through
143
+ AWS auth. OpenCode `provider: bedrock`.
144
+
145
+ # -- Other --
146
+ - id: mistral
147
+ label: "Mistral AI (API key)"
148
+ cli: opencode
149
+ auth_kind: api_key
150
+ secret_refs: [MISTRAL_API_KEY]
151
+ notes: |
152
+ Mistral-Large, Codestral, Pixtral. OpenCode `provider: mistral`.
153
+
154
+ - id: xai
155
+ label: "xAI Grok (Grok-2, Grok-Beta)"
156
+ cli: opencode
157
+ auth_kind: api_key
158
+ secret_refs: [XAI_API_KEY]
159
+ notes: |
160
+ Grok-2 / Grok-Beta via xAI's API. OpenCode `provider: xai`.
161
+
162
+ - id: github_copilot
163
+ label: "GitHub Copilot Chat (via gh extension)"
164
+ cli: gh_copilot
165
+ auth_kind: oauth
166
+ secret_refs: []
167
+ notes: |
168
+ Talk to Copilot from the terminal — `gh copilot suggest "..."` and
169
+ `gh copilot explain "..."`. Uses your GitHub OAuth.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentikos/omega-os",
3
- "version": "0.19.21",
3
+ "version": "0.19.23",
4
4
  "description": "Omega OS — installable agentic operating system with verified-completion orchestration. Event-sourced engine, 8-block rack, autonomous agents, MCP.",
5
5
  "bin": {
6
6
  "omega-os": "bin/omega-os.js"