@agentikos/omega-os 0.19.41 → 0.19.43
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/bootstrap/lib/__pycache__/llm-clis.cpython-313.pyc +0 -0
- package/bootstrap/lib/common.sh +6 -0
- package/bootstrap/lib/llm-clis.py +6 -0
- package/bootstrap/lib/manifest-helpers.py +110 -0
- package/bootstrap/lib/steps.sh +230 -26
- package/bootstrap/templates/aisb/CLAUDE.md +13 -0
- package/install.sh +8 -2
- package/omega/Agentik_Engine/omega_engine/__init__.py +1 -1
- package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/hermes.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/paperclip_bridge.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/personas.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/provider.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/tui.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +44 -7
- package/omega/Agentik_Engine/omega_engine/hermes.py +43 -1
- package/omega/Agentik_Engine/omega_engine/paperclip_bridge.py +22 -0
- package/omega/Agentik_Engine/omega_engine/personas.py +11 -3
- package/omega/Agentik_Engine/omega_engine/provider.py +18 -3
- package/omega/Agentik_Engine/omega_engine/tmux.py +192 -42
- package/omega/Agentik_Engine/omega_engine/tui.py +8 -7
- package/omega/Agentik_Engine/pyproject.toml +1 -1
- package/omega/Agentik_Engine/tests/__pycache__/test_install_steps_v0_19_43.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_steps_v0_19_43.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_palette.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_palette.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_v19_43_fixes.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_v19_43_fixes.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/test_install_steps_v0_19_43.py +242 -0
- package/omega/Agentik_Engine/tests/test_installer_wiring.py +128 -1
- package/omega/Agentik_Engine/tests/test_tmux_palette.py +158 -0
- package/omega/Agentik_Engine/tests/test_v19_43_fixes.py +265 -0
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/omega/Agentik_SSOT/docs/AUDIT-V0.19.43.md +92 -0
- package/omega/Agentik_SSOT/rules/audit-gates.md +2 -2
- package/omega/Agentik_SSOT/rules/constitution.md +18 -0
- package/omega/Agentik_SSOT/rules/three-laws.md +2 -0
- package/package.json +1 -1
|
@@ -271,6 +271,31 @@ def cmd_doctor(args: argparse.Namespace) -> int:
|
|
|
271
271
|
except Exception as exc: # noqa: BLE001
|
|
272
272
|
line("warn", f"provider auth check failed: {exc}")
|
|
273
273
|
|
|
274
|
+
# v0.19.43 — Hermès budget-isolation check. Hermès SHOULD use its own
|
|
275
|
+
# paid Anthropic key (ANTHROPIC_API_KEY_HERMES) so its calls don't
|
|
276
|
+
# share the AISB Max OAuth pool. See omega_engine/hermes.py::build_env
|
|
277
|
+
# for the 3-tier fallback. When path (3) — Max OAuth — is the live
|
|
278
|
+
# path, surface it here so the operator knows isolation is OFF.
|
|
279
|
+
try:
|
|
280
|
+
from omega_engine.vault import vault_read as _vread
|
|
281
|
+
_hermes_vault_key = (_vread(home, "ANTHROPIC_API_KEY_HERMES") or "").strip()
|
|
282
|
+
_hermes_env_key = (_os.environ.get("ANTHROPIC_API_KEY") or "").strip()
|
|
283
|
+
if _hermes_vault_key:
|
|
284
|
+
line("ok", "Hermès budget isolation: vault key present "
|
|
285
|
+
"(ANTHROPIC_API_KEY_HERMES)")
|
|
286
|
+
elif _hermes_env_key:
|
|
287
|
+
line("warn", "Hermès budget isolation: shell env key only "
|
|
288
|
+
"(ANTHROPIC_API_KEY) — works but couples Hermès to "
|
|
289
|
+
"whatever the env points at. Consider wiring the "
|
|
290
|
+
"vault key for permanent isolation.")
|
|
291
|
+
else:
|
|
292
|
+
line("warn", "Hermès budget isolation OFF — no vault key, no env "
|
|
293
|
+
"key. Hermès will fall back to Max OAuth (charges "
|
|
294
|
+
"your Max sub). Wire with: "
|
|
295
|
+
"`omega vault write ANTHROPIC_API_KEY_HERMES sk-ant-...`")
|
|
296
|
+
except Exception as exc: # noqa: BLE001
|
|
297
|
+
line("warn", f"Hermès isolation check failed: {exc}")
|
|
298
|
+
|
|
274
299
|
# 6c1. LLM CLIs installed on the host
|
|
275
300
|
section("llm clis")
|
|
276
301
|
import shutil as _shu
|
|
@@ -2885,8 +2910,11 @@ def cmd_aisb(args: argparse.Namespace) -> int:
|
|
|
2885
2910
|
from omega_engine import tmux
|
|
2886
2911
|
sub = getattr(args, "aisb_cmd", None) or "chat"
|
|
2887
2912
|
if sub in ("chat", None):
|
|
2913
|
+
# v0.19.43 — AISB-chat is now a WINDOW inside the Omega master
|
|
2914
|
+
# session (Omega:aisb), not a standalone session. spawn_aisb_chat
|
|
2915
|
+
# returns "Omega:aisb" so we pass that as the switch target.
|
|
2888
2916
|
return _attach_or_spawn_chat(
|
|
2889
|
-
"
|
|
2917
|
+
"Omega:aisb",
|
|
2890
2918
|
spawn_fn=tmux.spawn_aisb_chat,
|
|
2891
2919
|
label="AISB master (Claude Code on Max OAuth)",
|
|
2892
2920
|
)
|
|
@@ -3175,14 +3203,14 @@ def _legacy_fzf_menu(_args: argparse.Namespace) -> int:
|
|
|
3175
3203
|
subprocess.run(["tmux", "kill-session", "-t", "Omega"])
|
|
3176
3204
|
return 0
|
|
3177
3205
|
|
|
3178
|
-
# Chat sessions —
|
|
3206
|
+
# Chat sessions — windows inside Omega master (v0.19.43).
|
|
3179
3207
|
if action == "open:aisb":
|
|
3180
|
-
tmux.spawn_aisb_chat(HOME, force_replace=False)
|
|
3181
|
-
subprocess.run(["tmux", "switch-client", "-t",
|
|
3208
|
+
target = tmux.spawn_aisb_chat(HOME, force_replace=False)
|
|
3209
|
+
subprocess.run(["tmux", "switch-client", "-t", target])
|
|
3182
3210
|
continue
|
|
3183
3211
|
if action == "open:hermes":
|
|
3184
|
-
tmux.spawn_hermes_chat(HOME, force_replace=False)
|
|
3185
|
-
subprocess.run(["tmux", "switch-client", "-t",
|
|
3212
|
+
target = tmux.spawn_hermes_chat(HOME, force_replace=False)
|
|
3213
|
+
subprocess.run(["tmux", "switch-client", "-t", target])
|
|
3186
3214
|
continue
|
|
3187
3215
|
|
|
3188
3216
|
# Inline actions — run in the menu pane, prompt to return.
|
|
@@ -3494,6 +3522,13 @@ def cmd_switch(args: argparse.Namespace) -> int:
|
|
|
3494
3522
|
"qwen_code": "qwen",
|
|
3495
3523
|
"continue_dev": "cn",
|
|
3496
3524
|
"aider": "aider",
|
|
3525
|
+
# v0.19.43 — providers installable via the CLI catalog that were
|
|
3526
|
+
# missing from `omega switch`. Each maps to its bin_name from
|
|
3527
|
+
# bootstrap/lib/llm-clis.py::_CATALOG.
|
|
3528
|
+
"glm_sdk": "zhipuai", # Python SDK — no binary, kept for completeness
|
|
3529
|
+
"ollama": "ollama",
|
|
3530
|
+
"lm_studio": "lms",
|
|
3531
|
+
"gh_copilot": "gh", # extension on top of gh
|
|
3497
3532
|
}
|
|
3498
3533
|
if not getattr(args, "provider", None):
|
|
3499
3534
|
print(f" active provider: {_active_provider()}")
|
|
@@ -3598,10 +3633,12 @@ def cmd_hermes(args: argparse.Namespace) -> int:
|
|
|
3598
3633
|
|
|
3599
3634
|
# v0.19.15 — `omega hermes` with no subcommand opens the Hermès chat
|
|
3600
3635
|
# tmux session. Same UX as bare `omega` → AISB chat, but for L2.
|
|
3636
|
+
# v0.19.43 — Hermes-chat is now a WINDOW inside the Omega master
|
|
3637
|
+
# session (Omega:hermes); spawn_hermes_chat returns that ref.
|
|
3601
3638
|
if sub is None or sub == "chat":
|
|
3602
3639
|
from omega_engine import tmux as _tx
|
|
3603
3640
|
return _attach_or_spawn_chat(
|
|
3604
|
-
"
|
|
3641
|
+
"Omega:hermes",
|
|
3605
3642
|
spawn_fn=_tx.spawn_hermes_chat,
|
|
3606
3643
|
label="Hermès meta-companion chat",
|
|
3607
3644
|
)
|
|
@@ -262,6 +262,39 @@ def build_env(
|
|
|
262
262
|
calls through your Max sub.
|
|
263
263
|
|
|
264
264
|
Caller env wins via ``extra``.
|
|
265
|
+
|
|
266
|
+
──────────────────────────────────────────────────────────────────
|
|
267
|
+
IMPORTANT — Hermès credential resolution (v0.19.43 clarification)
|
|
268
|
+
──────────────────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
Hermès SHOULD use ANTHROPIC_API_KEY_HERMES from the vault for budget
|
|
271
|
+
isolation from the AISB Max OAuth. The 3-tier fallback below is a
|
|
272
|
+
SAFETY NET for the edge case where the vault key is missing but the
|
|
273
|
+
operator still wants Hermès to work (during initial setup, debugging,
|
|
274
|
+
or a one-off command before they have wired the paid key).
|
|
275
|
+
|
|
276
|
+
Fallback order (first non-empty wins):
|
|
277
|
+
|
|
278
|
+
1. ANTHROPIC_API_KEY_HERMES (vault) ← preferred, isolated budget
|
|
279
|
+
2. ANTHROPIC_API_KEY (shell env) ← shared env, semi-isolated
|
|
280
|
+
3. CLAUDE_CODE_OAUTH_TOKEN (Max OAuth) ← LAST RESORT, breaks isolation
|
|
281
|
+
|
|
282
|
+
Hierarchy intent:
|
|
283
|
+
* Path (1) is the contract Hermès was designed to honor.
|
|
284
|
+
* Path (2) lets a developer ``ANTHROPIC_API_KEY=sk-... omega hermes``
|
|
285
|
+
without writing to the vault.
|
|
286
|
+
* Path (3) preserves "Hermès still works on a fresh install before
|
|
287
|
+
the user has wired their key" — at the cost of charging the user's
|
|
288
|
+
Max sub for what should be a paid-API call.
|
|
289
|
+
|
|
290
|
+
``omega doctor`` warns when Hermès would fall through to path (3) in
|
|
291
|
+
practice (vault empty AND ``ANTHROPIC_API_KEY`` unset). The persistent
|
|
292
|
+
Hermès chat (``step_hermes_session`` in bootstrap/lib/steps.sh) SKIPS
|
|
293
|
+
its spawn entirely on missing vault key — it does NOT fall through to
|
|
294
|
+
Max OAuth for 24/7 runtime use, because an always-on Max-billed Hermès
|
|
295
|
+
is rarely what the operator wants. Only the interactive on-demand
|
|
296
|
+
``omega hermes`` path uses this fallback chain.
|
|
297
|
+
──────────────────────────────────────────────────────────────────
|
|
265
298
|
"""
|
|
266
299
|
env = {**os.environ}
|
|
267
300
|
# 1. Vault first.
|
|
@@ -279,10 +312,19 @@ def build_env(
|
|
|
279
312
|
if not key:
|
|
280
313
|
key = (os.environ.get("ANTHROPIC_API_KEY") or "").strip()
|
|
281
314
|
# 3. Last-resort OAuth (preserves the old behaviour without making
|
|
282
|
-
# Hermès depend on it).
|
|
315
|
+
# Hermès depend on it). When this branch fires, log a warning to
|
|
316
|
+
# stderr so the operator knows budget isolation is OFF — Hermès is
|
|
317
|
+
# now sharing the Max OAuth pool with AISB+Oracle+Workers. The
|
|
318
|
+
# `omega doctor` `personas` / `providers` section will also surface
|
|
319
|
+
# this drift.
|
|
283
320
|
if not key:
|
|
284
321
|
try:
|
|
285
322
|
env["CLAUDE_CODE_OAUTH_TOKEN"] = claude_oauth_token(creds_path)
|
|
323
|
+
import sys as _sys
|
|
324
|
+
print(" ⚠️ Hermès budget isolation OFF — falling back to "
|
|
325
|
+
"Max OAuth. Run `omega vault write "
|
|
326
|
+
"ANTHROPIC_API_KEY_HERMES sk-ant-...` to restore.",
|
|
327
|
+
file=_sys.stderr)
|
|
286
328
|
except Exception: # noqa: BLE001
|
|
287
329
|
pass
|
|
288
330
|
if key:
|
|
@@ -292,6 +292,28 @@ def send_heartbeat(
|
|
|
292
292
|
return True
|
|
293
293
|
|
|
294
294
|
|
|
295
|
+
def heartbeat_on_spawn(agent_id: str, project: str | None = None) -> None:
|
|
296
|
+
"""v0.19.43 — call this from spawn_worker / spawn_oracle right after
|
|
297
|
+
the tmux session is created. Drops a one-shot heartbeat to Paperclip
|
|
298
|
+
so the dashboard's live-agent indicator wakes up immediately instead
|
|
299
|
+
of waiting for the agent's first explicit heartbeat call.
|
|
300
|
+
|
|
301
|
+
Never raises — if Paperclip is not installed or not running, this is
|
|
302
|
+
a no-op. Safe to call from any spawn path.
|
|
303
|
+
"""
|
|
304
|
+
try:
|
|
305
|
+
hb = Heartbeat(
|
|
306
|
+
agent_id=agent_id,
|
|
307
|
+
project=project or "",
|
|
308
|
+
session=os.environ.get("TMUX_SESSION", ""),
|
|
309
|
+
status="alive",
|
|
310
|
+
summary="spawned",
|
|
311
|
+
)
|
|
312
|
+
send_heartbeat(hb)
|
|
313
|
+
except Exception:
|
|
314
|
+
pass # silent fallback — bridge is optional infrastructure
|
|
315
|
+
|
|
316
|
+
|
|
295
317
|
# ---------------------------------------------------------------------------
|
|
296
318
|
# Registration — write Paperclip's agent + company config from OmegaOS
|
|
297
319
|
# ---------------------------------------------------------------------------
|
|
@@ -55,6 +55,14 @@ _LLM_PERSONA_PATHS: dict[str, list[str]] = {
|
|
|
55
55
|
"continue_dev": [".continue/CONTEXT.md"],
|
|
56
56
|
"aider": ["CONVENTIONS.md"],
|
|
57
57
|
"hermes": ["HERMES.md"],
|
|
58
|
+
# v0.19.43 — 4 CLIs were installable but had no persona file. Without
|
|
59
|
+
# this mapping, write_all_personas() silently skipped them and the
|
|
60
|
+
# CLI would launch with no Omega context. Each maps to the closest
|
|
61
|
+
# convention for that CLI.
|
|
62
|
+
"glm_sdk": ["GLM.md"], # Zhipu GLM SDK — bespoke marker
|
|
63
|
+
"ollama": ["OLLAMA.md"], # Ollama local runtime
|
|
64
|
+
"lm_studio": ["LM_STUDIO.md"], # LM Studio local
|
|
65
|
+
"gh_copilot": ["CLAUDE.md"], # gh copilot piggy-backs on CLAUDE.md
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
|
|
@@ -98,7 +106,7 @@ You sit at L4 or L5 depending on context.
|
|
|
98
106
|
$OMEGA_HOME = ~/Omega
|
|
99
107
|
├── Agentik_Engine/ ← Python engine (omega CLI)
|
|
100
108
|
├── Agentik_SSOT/ ← source of truth
|
|
101
|
-
│ ├── skills/ ←
|
|
109
|
+
│ ├── skills/ ← 18 audits + orchestrators + /newcmd
|
|
102
110
|
│ ├── agents/aisb/ ← 13 AISB persona files
|
|
103
111
|
│ ├── personas/OMEGAOS-CONTEXT.md ← THIS FILE
|
|
104
112
|
│ ├── docs/LAYERS.md ← formal L1-L5 architecture
|
|
@@ -122,11 +130,11 @@ $OMEGA_HOME = ~/Omega
|
|
|
122
130
|
| Status / health | `omega doctor` / `omega status` |
|
|
123
131
|
| Session manager (tmux) | `omega` (no args) |
|
|
124
132
|
|
|
125
|
-
## Quality Arsenal (
|
|
133
|
+
## Quality Arsenal (18 forensic audits)
|
|
126
134
|
|
|
127
135
|
`/codeaudit /flowaudit /uiuxaudit /debugaudit /featureaudit /perfaudit
|
|
128
136
|
/secaudit /a11yaudit /seoaudit /dataaudit /apiaudit /copyaudit /dxaudit
|
|
129
|
-
/motionaudit /automationaudit /logicaudit /retentionaudit`
|
|
137
|
+
/motionaudit /automationaudit /logicaudit /retentionaudit /refontaudit`
|
|
130
138
|
|
|
131
139
|
Each: Gestalt clarity gate, Popper falsification, hinge point 10x
|
|
132
140
|
scrutiny, auto-fix, auto-re-audit, scored /100.
|
|
@@ -440,13 +440,28 @@ class _OpenAICompatProvider:
|
|
|
440
440
|
self._base_url = base_url or self._default_base_url
|
|
441
441
|
|
|
442
442
|
def _resolve_key(self) -> str:
|
|
443
|
-
|
|
444
|
-
if
|
|
443
|
+
# Constructor-injected key always wins.
|
|
444
|
+
if self._api_key:
|
|
445
|
+
return self._api_key
|
|
446
|
+
# v0.19.43 — try the encrypted vault first, then fall back to env.
|
|
447
|
+
# The vault is where step_providers stores api keys provided via
|
|
448
|
+
# the install manifest; env is the legacy / fallback path.
|
|
449
|
+
try:
|
|
450
|
+
from omega_engine.vault import vault_read
|
|
451
|
+
from pathlib import Path
|
|
452
|
+
home = Path(os.environ.get("OMEGA_HOME", str(Path.home() / "Omega")))
|
|
453
|
+
v = vault_read(home, self._env_var)
|
|
454
|
+
if v and v.strip():
|
|
455
|
+
return v.strip()
|
|
456
|
+
except Exception:
|
|
457
|
+
pass
|
|
458
|
+
key = os.environ.get(self._env_var)
|
|
459
|
+
if not key or not key.strip():
|
|
445
460
|
raise RuntimeError(
|
|
446
461
|
f"{type(self).__name__} requires an API key. "
|
|
447
462
|
f"Pass api_key=... or set {self._env_var} in the environment."
|
|
448
463
|
)
|
|
449
|
-
return key
|
|
464
|
+
return key.strip()
|
|
450
465
|
|
|
451
466
|
def _build_messages(self, req: AgentRequest) -> list[dict[str, str]]:
|
|
452
467
|
prompt = req.prompt
|
|
@@ -385,30 +385,37 @@ def spawn_chat_in_omega(
|
|
|
385
385
|
|
|
386
386
|
def spawn_aisb_chat(omega_home: str | Path | None = None,
|
|
387
387
|
force_replace: bool = False) -> str:
|
|
388
|
-
"""Spawn the AISB master chat
|
|
388
|
+
"""Spawn the AISB master chat as a WINDOW inside the Omega master session.
|
|
389
389
|
|
|
390
|
-
v0.19.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
390
|
+
v0.19.43 — refactored to use the window-based pattern via
|
|
391
|
+
:func:`spawn_chat_in_omega` so AISB-chat lives UNDER the persistent
|
|
392
|
+
Omega master session (single tmux lifecycle, detach returns to the
|
|
393
|
+
Omega menu) instead of as a standalone tmux session. This honors the
|
|
394
|
+
v0.19.31+ design promise: "everything under Omega".
|
|
395
|
+
|
|
396
|
+
Returns the full tmux ref ``"Omega:aisb"`` — callers can use it
|
|
397
|
+
directly with ``tmux switch-client -t`` / ``tmux attach -t``.
|
|
395
398
|
|
|
396
399
|
Auth: Claude Max OAuth (inherited from the user's shell env, no env
|
|
397
400
|
override needed — `claude` reads `~/.claude/.credentials.json`).
|
|
398
401
|
|
|
399
|
-
``force_replace=True`` kills any existing
|
|
400
|
-
the
|
|
401
|
-
|
|
402
|
+
``force_replace=True`` kills any existing aisb window first — used
|
|
403
|
+
by the menu's "+ New" action so the user gets a fresh window each
|
|
404
|
+
time.
|
|
402
405
|
"""
|
|
403
|
-
name = "AISB-chat"
|
|
404
406
|
home = Path(omega_home or os.environ.get("OMEGA_HOME")
|
|
405
407
|
or Path.home() / "Omega")
|
|
408
|
+
# Ensure the Omega master session exists — spawn_chat_in_omega tries
|
|
409
|
+
# to do this too, but its fallback uses ctx_dir.parent.parent.parent
|
|
410
|
+
# which doesn't always resolve to OMEGA_HOME. Be explicit.
|
|
411
|
+
spawn_omega_master(home)
|
|
406
412
|
persona = home / "Agentik_SSOT" / "agents" / "aisb" / "CLAUDE.md"
|
|
407
413
|
ctx_dir = _ensure_chat_context_dir(home, "aisb-master", persona)
|
|
408
|
-
|
|
409
|
-
|
|
414
|
+
spawn_chat_in_omega(
|
|
415
|
+
"aisb", ctx_dir=ctx_dir, run_command="claude",
|
|
410
416
|
force_replace=force_replace,
|
|
411
417
|
)
|
|
418
|
+
return "Omega:aisb"
|
|
412
419
|
|
|
413
420
|
|
|
414
421
|
def spawn_omega_master(omega_home: str | Path | None = None) -> str:
|
|
@@ -448,19 +455,31 @@ def spawn_omega_master(omega_home: str | Path | None = None) -> str:
|
|
|
448
455
|
|
|
449
456
|
def spawn_hermes_chat(omega_home: str | Path | None = None,
|
|
450
457
|
force_replace: bool = False) -> str:
|
|
451
|
-
"""Spawn the Hermès chat
|
|
452
|
-
|
|
458
|
+
"""Spawn the Hermès chat as a WINDOW inside the Omega master session.
|
|
459
|
+
|
|
460
|
+
v0.19.43 — refactored to use the window-based pattern via
|
|
461
|
+
:func:`spawn_chat_in_omega` to match :func:`spawn_aisb_chat` and the
|
|
462
|
+
menu TUI (single tmux lifecycle, detach returns to the Omega menu).
|
|
463
|
+
|
|
464
|
+
Credential isolation is PRESERVED — Hermès reads
|
|
465
|
+
``ANTHROPIC_API_KEY_HERMES`` from the vault to keep its budget
|
|
466
|
+
isolated from the AISB Max OAuth. If the vault key is missing we
|
|
467
|
+
fall back to ``ANTHROPIC_API_KEY`` in the shell env, and finally to
|
|
468
|
+
plain ``claude`` (Max OAuth) with a marker file dropped in the
|
|
469
|
+
context dir so the operator sees the message after attaching.
|
|
453
470
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
``ANTHROPIC_API_KEY`` (from vault) before launching `claude`. Same
|
|
457
|
-
shell-stays-alive pattern.
|
|
471
|
+
Returns ``"Omega:hermes"`` so callers can use it with
|
|
472
|
+
``tmux switch-client -t`` / ``tmux attach -t`` directly.
|
|
458
473
|
"""
|
|
459
|
-
name = "Hermes-chat"
|
|
460
474
|
home = Path(omega_home or os.environ.get("OMEGA_HOME")
|
|
461
475
|
or Path.home() / "Omega")
|
|
476
|
+
# Ensure the Omega master session exists — see spawn_aisb_chat.
|
|
477
|
+
spawn_omega_master(home)
|
|
462
478
|
persona = home / "Agentik_SSOT" / "docs" / "LAYERS.md"
|
|
463
479
|
ctx_dir = _ensure_chat_context_dir(home, "hermes", persona)
|
|
480
|
+
# Credential isolation — vault key first, env fallback, Max OAuth
|
|
481
|
+
# last-resort. MUST stay in this function: the budget-isolation
|
|
482
|
+
# contract belongs to Hermès, not to the generic spawn helper.
|
|
464
483
|
try:
|
|
465
484
|
from omega_engine.vault import vault_read
|
|
466
485
|
api_key = (vault_read(home, "ANTHROPIC_API_KEY_HERMES") or "").strip()
|
|
@@ -479,10 +498,11 @@ def spawn_hermes_chat(omega_home: str | Path | None = None,
|
|
|
479
498
|
else:
|
|
480
499
|
# Single-line shell statement; send-keys passes it as-is.
|
|
481
500
|
run_cmd = f"ANTHROPIC_API_KEY={api_key} claude"
|
|
482
|
-
|
|
483
|
-
|
|
501
|
+
spawn_chat_in_omega(
|
|
502
|
+
"hermes", ctx_dir=ctx_dir, run_command=run_cmd,
|
|
484
503
|
force_replace=force_replace,
|
|
485
504
|
)
|
|
505
|
+
return "Omega:hermes"
|
|
486
506
|
|
|
487
507
|
|
|
488
508
|
# ---------------------------------------------------------------------------
|
|
@@ -589,10 +609,15 @@ bind-key Z display-popup -E -w 100% -h 100% "omega tmux menu"
|
|
|
589
609
|
bind-key S choose-tree -Zs
|
|
590
610
|
|
|
591
611
|
# Option+/ → session switcher (fzf popup, pick any live tmux session)
|
|
592
|
-
# Option+z → Omega action menu (
|
|
612
|
+
# Option+z → Omega action menu (popup direct like tmux-claude)
|
|
593
613
|
# macOS Option key → enable "Use Option as Meta" in your terminal.
|
|
594
|
-
|
|
595
|
-
|
|
614
|
+
#
|
|
615
|
+
# v0.19.42 — both binds use absolute paths (substituted at write-time)
|
|
616
|
+
# because tmux's popup spawns a non-interactive shell that doesn't source
|
|
617
|
+
# .bashrc/.zshrc, so bare `omega` is not in PATH. The placeholder
|
|
618
|
+
# __OMEGA_BIN__ is replaced by write_default_config().
|
|
619
|
+
bind-key -n M-/ display-popup -E -w 100% -h 100% "__OMEGA_BIN__ tmux switcher"
|
|
620
|
+
bind-key -n M-z display-popup -E -w 100% -h 100% "__OMEGA_BIN__ menu-tui"
|
|
596
621
|
|
|
597
622
|
# ════════════════════════════════════════════════════════════════════
|
|
598
623
|
# Pixel/CRT amber theme (tmux-claude inspired)
|
|
@@ -720,8 +745,16 @@ bind-key r source-file ~/.tmux.conf \\; display-message "tmux.conf reloaded"
|
|
|
720
745
|
# popup -E runs the command in a tmux popup overlay (modal). The popup
|
|
721
746
|
# closes when the command exits, returning the user to their previous
|
|
722
747
|
# pane. -w 100% -h 100% gives a full-screen popup (tmux-claude style).
|
|
723
|
-
|
|
724
|
-
|
|
748
|
+
#
|
|
749
|
+
# v0.19.42 — both binds use the ABSOLUTE PATH (substituted at write-time
|
|
750
|
+
# from __OMEGA_BIN__) because tmux display-popup spawns a non-interactive
|
|
751
|
+
# shell that doesn't source .bashrc/.zshrc — bare `omega` is not in PATH.
|
|
752
|
+
# Previously M-z used `run-shell -b` which executed in the background, so
|
|
753
|
+
# `tmux switch-client` ran in a forked process and never affected the
|
|
754
|
+
# foreground client. Result: "ça lance rien du tout". Fixed by going
|
|
755
|
+
# direct: display-popup the menu just like tmux-claude does.
|
|
756
|
+
bind-key -n M-/ display-popup -E -w 100% -h 100% "__OMEGA_BIN__ tmux switcher"
|
|
757
|
+
bind-key -n M-z display-popup -E -w 100% -h 100% "__OMEGA_BIN__ menu-tui"
|
|
725
758
|
|
|
726
759
|
# ════════════════════════════════════════════════════════════════════
|
|
727
760
|
# Pixel/CRT amber theme (tmux-claude inspired)
|
|
@@ -755,10 +788,23 @@ set -g mode-style "fg=default,bg=colour137"
|
|
|
755
788
|
"""
|
|
756
789
|
|
|
757
790
|
|
|
791
|
+
_OMEGA_BIN_PLACEHOLDER = "__OMEGA_BIN__"
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
def _resolve_omega_bin(omega_home: Path) -> str:
|
|
795
|
+
"""Return the absolute path to the omega CLI binary for the given
|
|
796
|
+
OMEGA_HOME. Used to substitute the __OMEGA_BIN__ placeholder in the
|
|
797
|
+
bundled tmux configs so tmux's display-popup (which spawns a
|
|
798
|
+
non-interactive shell) doesn't have to rely on PATH propagation.
|
|
799
|
+
"""
|
|
800
|
+
return str(omega_home / "Agentik_Tools" / "bin" / "omega")
|
|
801
|
+
|
|
802
|
+
|
|
758
803
|
def write_default_config(
|
|
759
804
|
target: str | Path | None = None,
|
|
760
805
|
*,
|
|
761
806
|
profile: str = "minimal",
|
|
807
|
+
omega_home: str | Path | None = None,
|
|
762
808
|
) -> Path:
|
|
763
809
|
"""Write a tmux config to ``target``.
|
|
764
810
|
|
|
@@ -773,52 +819,156 @@ def write_default_config(
|
|
|
773
819
|
or replace theirs entirely. We never silently overwrite ``~/.tmux.conf``.
|
|
774
820
|
Use :func:`install_into_home_tmux_conf` to do that explicitly + with
|
|
775
821
|
backup.
|
|
822
|
+
|
|
823
|
+
v0.19.42 — substitutes ``__OMEGA_BIN__`` placeholder in the M-z /
|
|
824
|
+
M-/ keybinds with the absolute path of the omega CLI binary, so the
|
|
825
|
+
binds work even when tmux's display-popup shell doesn't have
|
|
826
|
+
``$OMEGA_HOME/Agentik_Tools/bin`` in PATH.
|
|
776
827
|
"""
|
|
828
|
+
home = Path(omega_home or os.environ.get(
|
|
829
|
+
"OMEGA_HOME", str(Path.home() / "Omega")))
|
|
777
830
|
if target is None:
|
|
778
|
-
home = Path(os.environ.get("OMEGA_HOME",
|
|
779
|
-
str(Path.home() / "Omega")))
|
|
780
831
|
target = home / "Agentik_Tools" / "tmux.conf"
|
|
781
832
|
target = Path(target)
|
|
782
833
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
783
|
-
|
|
834
|
+
template = _PRO_CONFIG if profile == "pro" else _DEFAULT_CONFIG
|
|
835
|
+
content = template.replace(_OMEGA_BIN_PLACEHOLDER,
|
|
836
|
+
_resolve_omega_bin(home))
|
|
784
837
|
target.write_text(content)
|
|
785
838
|
return target
|
|
786
839
|
|
|
787
840
|
|
|
841
|
+
def _is_tmux_claude_config(text: str) -> bool:
|
|
842
|
+
"""Detect whether a ``~/.tmux.conf`` already hosts a tmux-claude
|
|
843
|
+
install (github.com/agentik-os/tmux-claude). If so, we MUST NOT
|
|
844
|
+
overwrite it — tmux-claude's session-manager-v2.sh provides the
|
|
845
|
+
user's Alt+Z / Alt+/ session navigator already, and clobbering it
|
|
846
|
+
breaks their setup.
|
|
847
|
+
|
|
848
|
+
Markers checked:
|
|
849
|
+
* literal ``tmux-claude`` mention (their banner / source-file
|
|
850
|
+
comment)
|
|
851
|
+
* ``session-manager-v2.sh`` (their flagship menu script)
|
|
852
|
+
"""
|
|
853
|
+
return ("tmux-claude" in text
|
|
854
|
+
or "session-manager-v2.sh" in text)
|
|
855
|
+
|
|
856
|
+
|
|
788
857
|
def install_into_home_tmux_conf(
|
|
789
858
|
*,
|
|
790
859
|
profile: str = "pro",
|
|
791
860
|
backup: bool = True,
|
|
792
861
|
omega_home: str | Path | None = None,
|
|
862
|
+
force: bool = False,
|
|
793
863
|
) -> dict[str, str]:
|
|
794
864
|
"""Write the chosen config to ``~/.tmux.conf`` with a timestamped backup.
|
|
795
865
|
|
|
796
|
-
|
|
797
|
-
|
|
866
|
+
v0.19.42 — RESPECTS tmux-claude. If ``~/.tmux.conf`` already hosts a
|
|
867
|
+
tmux-claude install, we DO NOT overwrite it. Instead we drop the
|
|
868
|
+
OmegaOS-specific keybinds at ``$OMEGA_HOME/Agentik_Tools/omega-tmux-add.conf``
|
|
869
|
+
and append one ``source-file`` line to ``~/.tmux.conf`` so both
|
|
870
|
+
coexist. Override with ``force=True``.
|
|
871
|
+
|
|
872
|
+
Returns:
|
|
873
|
+
* ``written`` — path of the file we wrote (~/.tmux.conf or the
|
|
874
|
+
add-on file)
|
|
875
|
+
* ``backup_path`` — backup of ~/.tmux.conf, empty if none
|
|
876
|
+
* ``bundled_copy`` — always written (the $OMEGA_HOME/Agentik_Tools/tmux.conf)
|
|
877
|
+
* ``profile`` — selected profile
|
|
878
|
+
* ``mode`` — "fresh" (we wrote a full conf) |
|
|
879
|
+
"preserved-tmux-claude" (we ADDED to the
|
|
880
|
+
existing tmux-claude conf without overwriting)
|
|
798
881
|
"""
|
|
799
882
|
import shutil as _sh
|
|
800
883
|
import time as _t
|
|
801
884
|
home_conf = Path.home() / ".tmux.conf"
|
|
802
|
-
backup_path = ""
|
|
803
|
-
if home_conf.exists() and backup:
|
|
804
|
-
ts = _t.strftime("%Y%m%d-%H%M%S")
|
|
805
|
-
backup_path = str(home_conf.with_suffix(f".conf.bak.{ts}"))
|
|
806
|
-
_sh.copy2(home_conf, backup_path)
|
|
807
|
-
content = _PRO_CONFIG if profile == "pro" else _DEFAULT_CONFIG
|
|
808
|
-
home_conf.write_text(content)
|
|
809
|
-
# Also drop it under $OMEGA_HOME so `omega tmux install` is the
|
|
810
|
-
# single source of truth that the engine sync mechanism can refresh.
|
|
811
|
-
_ = write_default_config(omega_home=None) if False else None
|
|
812
885
|
home = Path(omega_home or os.environ.get(
|
|
813
886
|
"OMEGA_HOME", str(Path.home() / "Omega")
|
|
814
887
|
))
|
|
888
|
+
template = _PRO_CONFIG if profile == "pro" else _DEFAULT_CONFIG
|
|
889
|
+
content = template.replace(_OMEGA_BIN_PLACEHOLDER,
|
|
890
|
+
_resolve_omega_bin(home))
|
|
891
|
+
|
|
892
|
+
# Always update the bundled copy under $OMEGA_HOME.
|
|
815
893
|
bundled = home / "Agentik_Tools" / "tmux.conf"
|
|
816
894
|
bundled.parent.mkdir(parents=True, exist_ok=True)
|
|
817
895
|
bundled.write_text(content)
|
|
896
|
+
|
|
897
|
+
backup_path = ""
|
|
898
|
+
if home_conf.exists() and backup:
|
|
899
|
+
ts = _t.strftime("%Y%m%d-%H%M%S")
|
|
900
|
+
backup_path = str(home_conf.with_suffix(f".conf.bak.{ts}"))
|
|
901
|
+
_sh.copy2(home_conf, backup_path)
|
|
902
|
+
|
|
903
|
+
# v0.19.42 — tmux-claude detection. If the user's ~/.tmux.conf is a
|
|
904
|
+
# tmux-claude install, leave it alone and provide an ADD-ON config.
|
|
905
|
+
if home_conf.exists() and not force:
|
|
906
|
+
existing = home_conf.read_text()
|
|
907
|
+
if _is_tmux_claude_config(existing):
|
|
908
|
+
addon_path = home / "Agentik_Tools" / "omega-tmux-add.conf"
|
|
909
|
+
addon_path.parent.mkdir(parents=True, exist_ok=True)
|
|
910
|
+
# Write a minimal OmegaOS-specific add-on: just the Ω bindings
|
|
911
|
+
# that don't collide with tmux-claude's M-z / M-/ (which we
|
|
912
|
+
# respect). Operator gets Omega via Ctrl-b z (prefix) or M-o.
|
|
913
|
+
addon = _omega_addon_config(home)
|
|
914
|
+
addon_path.write_text(addon)
|
|
915
|
+
# Append one source-file line if not already there.
|
|
916
|
+
source_line = f"source-file -q {addon_path}"
|
|
917
|
+
if source_line not in existing:
|
|
918
|
+
home_conf.write_text(existing.rstrip() + "\n\n"
|
|
919
|
+
+ "# OmegaOS — Ω bindings (added "
|
|
920
|
+
+ _t.strftime("%Y-%m-%d") + ")\n"
|
|
921
|
+
+ source_line + "\n")
|
|
922
|
+
return {
|
|
923
|
+
"written": str(addon_path),
|
|
924
|
+
"backup_path": backup_path,
|
|
925
|
+
"bundled_copy": str(bundled),
|
|
926
|
+
"profile": profile,
|
|
927
|
+
"mode": "preserved-tmux-claude",
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
# Fresh install (or --force): write the full OmegaOS conf.
|
|
931
|
+
home_conf.write_text(content)
|
|
818
932
|
return {
|
|
819
933
|
"written": str(home_conf),
|
|
820
934
|
"backup_path": backup_path,
|
|
821
935
|
"bundled_copy": str(bundled),
|
|
822
936
|
"profile": profile,
|
|
937
|
+
"mode": "fresh",
|
|
823
938
|
}
|
|
824
|
-
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
def _omega_addon_config(omega_home: Path) -> str:
|
|
942
|
+
"""The minimal OmegaOS keybinds + status-bar Ω indicator that we
|
|
943
|
+
can source from a tmux-claude config without conflict.
|
|
944
|
+
|
|
945
|
+
Conflicts with tmux-claude:
|
|
946
|
+
* M-z, M-/ → kept by tmux-claude (their session-manager-v2.sh)
|
|
947
|
+
* M-a → kept by tmux-claude (their AISB Command Center)
|
|
948
|
+
|
|
949
|
+
Free for OmegaOS:
|
|
950
|
+
* prefix+Ω → not bound by tmux-claude
|
|
951
|
+
* M-o → Alt+O for Omega menu (memorable)
|
|
952
|
+
* Ctrl-b Ω → prefix+Ω as an alternate
|
|
953
|
+
"""
|
|
954
|
+
omega_bin = _resolve_omega_bin(omega_home)
|
|
955
|
+
return f"""# ════════════════════════════════════════════════════════════════
|
|
956
|
+
# OmegaOS — additive bindings (sourced from ~/.tmux.conf)
|
|
957
|
+
# This file is generated by omega_engine.tmux. Safe to source alongside
|
|
958
|
+
# any existing tmux-claude install; we only bind keys tmux-claude leaves
|
|
959
|
+
# free.
|
|
960
|
+
#
|
|
961
|
+
# Free keys we use:
|
|
962
|
+
# * Alt+O → Omega menu (popup, full-screen)
|
|
963
|
+
# * Ctrl-b o → same, via prefix
|
|
964
|
+
# * Ctrl-b Ω → status-bar indicator only
|
|
965
|
+
#
|
|
966
|
+
# Reserved by tmux-claude (we DO NOT touch):
|
|
967
|
+
# * Alt+Z, Alt+/, Alt+A
|
|
968
|
+
# ════════════════════════════════════════════════════════════════
|
|
969
|
+
|
|
970
|
+
# Alt+O → Omega menu (popup, full-screen)
|
|
971
|
+
bind-key -n M-o display-popup -E -w 100% -h 100% "{omega_bin} menu-tui"
|
|
972
|
+
# Ctrl-b o → same via prefix
|
|
973
|
+
bind-key o display-popup -E -w 100% -h 100% "{omega_bin} menu-tui"
|
|
974
|
+
"""
|
|
@@ -67,7 +67,7 @@ except ImportError:
|
|
|
67
67
|
CATEGORIES: list[tuple[str, str]] = [
|
|
68
68
|
("chat", "Chat — AISB & Hermès"),
|
|
69
69
|
("projects", "Projects — Genesis & shells"),
|
|
70
|
-
("audits", "Audits — Quality Arsenal (
|
|
70
|
+
("audits", "Audits — Quality Arsenal (18 forensic)"),
|
|
71
71
|
("missions", "Missions — run with verified completion"),
|
|
72
72
|
("sessions", "Sessions — live tmux sessions"),
|
|
73
73
|
("accounts", "Accounts — Claude Max + provider auth"),
|
|
@@ -135,22 +135,23 @@ def dispatch_slash(line: str) -> CommandResult:
|
|
|
135
135
|
if cmd in ("detach", "d"):
|
|
136
136
|
return CommandResult(detach=True)
|
|
137
137
|
|
|
138
|
-
# Chat sessions
|
|
138
|
+
# Chat sessions — v0.19.43 are now WINDOWS inside the Omega master
|
|
139
|
+
# session (Omega:aisb, Omega:hermes), not standalone sessions.
|
|
139
140
|
if cmd == "aisb" or (cmd == "chat" and rest and rest[0] == "aisb"):
|
|
140
141
|
from omega_engine import tmux as _tx
|
|
141
|
-
_tx.spawn_aisb_chat(Path(os.environ.get(
|
|
142
|
+
target = _tx.spawn_aisb_chat(Path(os.environ.get(
|
|
142
143
|
"OMEGA_HOME", str(Path.home() / "Omega"))))
|
|
143
144
|
return CommandResult(
|
|
144
145
|
text=" AISB-chat spawned — switching client…",
|
|
145
|
-
session_jump=
|
|
146
|
+
session_jump=target,
|
|
146
147
|
)
|
|
147
148
|
if cmd == "hermes" or (cmd == "chat" and rest and rest[0] == "hermes"):
|
|
148
149
|
from omega_engine import tmux as _tx
|
|
149
|
-
_tx.spawn_hermes_chat(Path(os.environ.get(
|
|
150
|
+
target = _tx.spawn_hermes_chat(Path(os.environ.get(
|
|
150
151
|
"OMEGA_HOME", str(Path.home() / "Omega"))))
|
|
151
152
|
return CommandResult(
|
|
152
153
|
text=" Hermes-chat spawned — switching client…",
|
|
153
|
-
session_jump=
|
|
154
|
+
session_jump=target,
|
|
154
155
|
)
|
|
155
156
|
|
|
156
157
|
# Provider hot-swap
|
|
@@ -164,7 +165,7 @@ def dispatch_slash(line: str) -> CommandResult:
|
|
|
164
165
|
if rest:
|
|
165
166
|
return CommandResult(text=_run_omega("audit", *rest))
|
|
166
167
|
return CommandResult(text=(
|
|
167
|
-
" Quality Arsenal —
|
|
168
|
+
" Quality Arsenal — 18 forensic audits:\n"
|
|
168
169
|
" /codeaudit /uiuxaudit /flowaudit /debugaudit\n"
|
|
169
170
|
" /featureaudit /perfaudit /secaudit /a11yaudit\n"
|
|
170
171
|
" /seoaudit /dataaudit /apiaudit /copyaudit\n"
|
|
Binary file
|
|
Binary file
|