@agentikos/omega-os 0.19.17 → 0.19.19
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/steps.sh +10 -8
- 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__/tmux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +156 -27
- package/omega/Agentik_Engine/omega_engine/tmux.py +77 -20
- package/omega/Agentik_Engine/pyproject.toml +1 -1
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/package.json +1 -1
package/bootstrap/lib/steps.sh
CHANGED
|
@@ -21,16 +21,18 @@ step_preflight() {
|
|
|
21
21
|
|
|
22
22
|
# --- 10 -----------------------------------------------------------------------
|
|
23
23
|
step_system_deps() {
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
24
|
+
# System deps:
|
|
25
|
+
# - whiptail/newt — back-up menu (`omega menu`)
|
|
26
|
+
# - fzf — primary picker for the new session manager (`omega`)
|
|
27
|
+
# and required by the tmux-claude install script too
|
|
28
|
+
# whiptail lives in `whiptail` (Debian/Ubuntu), `newt` (RHEL/Fedora),
|
|
29
|
+
# `newt` (Homebrew). fzf is `fzf` on every platform.
|
|
28
30
|
local pkgs="python3 git tmux sqlite3 jq curl"
|
|
29
31
|
case "$OMEGA_PKG" in
|
|
30
|
-
apt) sudo apt-get update -qq && sudo apt-get install -y -qq $pkgs whiptail python3-venv python3-yaml ;;
|
|
31
|
-
dnf) sudo dnf install -y -q $pkgs newt python3-pyyaml ;;
|
|
32
|
-
brew) brew install $pkgs newt 2>/dev/null || true ;;
|
|
33
|
-
*) err "install manually: $pkgs (and whiptail/newt for the menu)"; return 1 ;;
|
|
32
|
+
apt) sudo apt-get update -qq && sudo apt-get install -y -qq $pkgs whiptail fzf python3-venv python3-yaml ;;
|
|
33
|
+
dnf) sudo dnf install -y -q $pkgs newt fzf python3-pyyaml ;;
|
|
34
|
+
brew) brew install $pkgs newt fzf 2>/dev/null || true ;;
|
|
35
|
+
*) err "install manually: $pkgs (and whiptail/newt + fzf for the menu)"; return 1 ;;
|
|
34
36
|
esac
|
|
35
37
|
|
|
36
38
|
# PyYAML — required by every install-side helper that reads the manifest
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2716,24 +2716,25 @@ def cmd_tmux(args: argparse.Namespace) -> int:
|
|
|
2716
2716
|
|
|
2717
2717
|
|
|
2718
2718
|
def cmd_aisb(args: argparse.Namespace) -> int:
|
|
2719
|
-
"""`omega aisb` —
|
|
2719
|
+
"""`omega aisb` — straight to the AISB master Claude Code session.
|
|
2720
2720
|
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2721
|
+
v0.19.19 — `omega aisb` (no subcommand) now does spawn+attach in one
|
|
2722
|
+
shot, identical to selecting "+ New AISB" from the `omega` session
|
|
2723
|
+
manager. Same Claude Code TUI, same persona via CLAUDE.md.
|
|
2724
|
+
|
|
2725
|
+
Sub-commands:
|
|
2726
|
+
chat — explicit alias for the default (back-compat)
|
|
2727
|
+
chat-loop — the legacy Python REPL (used by dispatched workers
|
|
2728
|
+
that want a programmatic single-turn API)
|
|
2724
2729
|
"""
|
|
2725
2730
|
from omega_engine import tmux
|
|
2726
|
-
sub = args
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
tmux.spawn_aisb_chat(home)
|
|
2734
|
-
print(f" spawned: {name}")
|
|
2735
|
-
print(f" attach: {tmux.attach_command(name)}")
|
|
2736
|
-
return 0
|
|
2731
|
+
sub = getattr(args, "aisb_cmd", None) or "chat"
|
|
2732
|
+
if sub in ("chat", None):
|
|
2733
|
+
return _attach_or_spawn_chat(
|
|
2734
|
+
"AISB-chat",
|
|
2735
|
+
spawn_fn=tmux.spawn_aisb_chat,
|
|
2736
|
+
label="AISB master (Claude Code on Max OAuth)",
|
|
2737
|
+
)
|
|
2737
2738
|
if sub == "chat-loop":
|
|
2738
2739
|
from omega_engine.aisb_chat import run_chat_loop
|
|
2739
2740
|
return run_chat_loop()
|
|
@@ -2783,21 +2784,149 @@ def _attach_or_spawn_chat(session_name: str, *,
|
|
|
2783
2784
|
return 0 # unreachable (execvp replaced the process)
|
|
2784
2785
|
|
|
2785
2786
|
|
|
2786
|
-
def
|
|
2787
|
-
"""
|
|
2787
|
+
def _session_age(unix_ts: int) -> str:
|
|
2788
|
+
"""Pretty-print 'now' / '5m ago' / '2h ago' / '3d ago'."""
|
|
2789
|
+
import time as _t
|
|
2790
|
+
delta = max(0, int(_t.time()) - int(unix_ts))
|
|
2791
|
+
if delta < 60:
|
|
2792
|
+
return "now"
|
|
2793
|
+
if delta < 3600:
|
|
2794
|
+
return f"{delta // 60}m ago"
|
|
2795
|
+
if delta < 86400:
|
|
2796
|
+
return f"{delta // 3600}h ago"
|
|
2797
|
+
return f"{delta // 86400}d ago"
|
|
2798
|
+
|
|
2788
2799
|
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2800
|
+
def cmd_menu(_args: argparse.Namespace) -> int:
|
|
2801
|
+
"""`omega` (no args) — open the OmegaOS session manager.
|
|
2802
|
+
|
|
2803
|
+
v0.19.19 redesign per the user's spec: bare `omega` no longer
|
|
2804
|
+
auto-attaches to AISB-chat. Instead it shows a fzf-driven menu:
|
|
2805
|
+
|
|
2806
|
+
┌─ Omega — Session Manager ──────────────────┐
|
|
2807
|
+
│ > AISB-chat now │
|
|
2808
|
+
│ Hermes-chat 5m ago │
|
|
2809
|
+
│ Causio-oracle 2h ago │
|
|
2810
|
+
│ ───────────────────────────────────────── │
|
|
2811
|
+
│ + New Claude Code session (AISB master) │
|
|
2812
|
+
│ + New Hermès session │
|
|
2813
|
+
└────────────────────────────────────────────┘
|
|
2814
|
+
|
|
2815
|
+
Pick a session → attach. Pick "+ New …" → spawn + attach. Esc / q
|
|
2816
|
+
→ exit cleanly.
|
|
2817
|
+
|
|
2818
|
+
Direct shortcuts kept:
|
|
2819
|
+
* `omega aisb` → straight to AISB-chat (no menu)
|
|
2820
|
+
* `omega hermes` → straight to Hermes-chat (no menu)
|
|
2821
|
+
* `omega menu` → legacy whiptail picker (advanced ops)
|
|
2794
2822
|
"""
|
|
2823
|
+
import os
|
|
2824
|
+
import shutil
|
|
2825
|
+
import subprocess
|
|
2795
2826
|
from omega_engine import tmux
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
)
|
|
2827
|
+
|
|
2828
|
+
# If we're already inside a tmux session, instruct the user — we can't
|
|
2829
|
+
# take over the current pane from a subprocess, but `omega aisb` /
|
|
2830
|
+
# `omega hermes` from inside tmux still print switch-client instructions.
|
|
2831
|
+
if os.environ.get("TMUX"):
|
|
2832
|
+
print(" You're already inside tmux. Use Ctrl+b z (tmux-claude")
|
|
2833
|
+
print(" session manager) or one of:")
|
|
2834
|
+
print(" tmux switch-client -t AISB-chat")
|
|
2835
|
+
print(" tmux switch-client -t Hermes-chat")
|
|
2836
|
+
return 0
|
|
2837
|
+
|
|
2838
|
+
if not shutil.which("tmux"):
|
|
2839
|
+
print(" tmux not installed — `brew install tmux` (macOS) or "
|
|
2840
|
+
"`apt-get install tmux` (Linux)")
|
|
2841
|
+
return 2
|
|
2842
|
+
|
|
2843
|
+
sessions = tmux.list_sessions(_omega_home())
|
|
2844
|
+
sessions.sort(key=lambda s: -int(s.created))
|
|
2845
|
+
|
|
2846
|
+
NEW_AISB = "__new_aisb__"
|
|
2847
|
+
NEW_HERMES = "__new_hermes__"
|
|
2848
|
+
|
|
2849
|
+
lines: list[tuple[str, str]] = [] # (display, action_key)
|
|
2850
|
+
if sessions:
|
|
2851
|
+
for s in sessions:
|
|
2852
|
+
attach_mark = " *" if s.attached else " "
|
|
2853
|
+
line = f"{attach_mark} {s.name:<32} {_session_age(s.created):<10} {s.category}"
|
|
2854
|
+
lines.append((line, s.name))
|
|
2855
|
+
lines.append(("──────────────────────────────────────────────",
|
|
2856
|
+
"__separator__"))
|
|
2857
|
+
lines.append((" + New Claude Code session (AISB master)", NEW_AISB))
|
|
2858
|
+
lines.append((" + New Hermès session", NEW_HERMES))
|
|
2859
|
+
|
|
2860
|
+
chosen_key: str | None = None
|
|
2861
|
+
if shutil.which("fzf"):
|
|
2862
|
+
# fzf path — prettiest UX. Header shows the title, separator line
|
|
2863
|
+
# is non-selectable (we filter it after).
|
|
2864
|
+
fzf_input = "\n".join(disp for disp, _ in lines)
|
|
2865
|
+
try:
|
|
2866
|
+
proc = subprocess.run(
|
|
2867
|
+
["fzf",
|
|
2868
|
+
"--prompt=omega › ",
|
|
2869
|
+
"--header=Omega Session Manager — Enter to attach, Esc to quit",
|
|
2870
|
+
"--layout=reverse",
|
|
2871
|
+
"--height=70%",
|
|
2872
|
+
"--border",
|
|
2873
|
+
"--no-multi"],
|
|
2874
|
+
input=fzf_input, capture_output=True, text=True,
|
|
2875
|
+
)
|
|
2876
|
+
if proc.returncode == 0:
|
|
2877
|
+
pick = proc.stdout.strip()
|
|
2878
|
+
for disp, key in lines:
|
|
2879
|
+
if disp == pick and key != "__separator__":
|
|
2880
|
+
chosen_key = key
|
|
2881
|
+
break
|
|
2882
|
+
except Exception as exc: # noqa: BLE001
|
|
2883
|
+
print(f" fzf failed: {exc}")
|
|
2884
|
+
return 2
|
|
2885
|
+
else:
|
|
2886
|
+
# Plain stdin fallback — numbered list.
|
|
2887
|
+
print()
|
|
2888
|
+
print(" Omega Session Manager")
|
|
2889
|
+
for i, (disp, _) in enumerate(lines, 1):
|
|
2890
|
+
print(f" {i:2}) {disp}")
|
|
2891
|
+
print()
|
|
2892
|
+
try:
|
|
2893
|
+
raw = input(" pick a number (Enter to quit): ").strip()
|
|
2894
|
+
except (EOFError, KeyboardInterrupt):
|
|
2895
|
+
return 0
|
|
2896
|
+
if not raw:
|
|
2897
|
+
return 0
|
|
2898
|
+
try:
|
|
2899
|
+
n = int(raw)
|
|
2900
|
+
except ValueError:
|
|
2901
|
+
print(f" not a number: {raw!r}")
|
|
2902
|
+
return 0
|
|
2903
|
+
if 1 <= n <= len(lines):
|
|
2904
|
+
chosen_key = lines[n - 1][1]
|
|
2905
|
+
if chosen_key == "__separator__":
|
|
2906
|
+
chosen_key = None
|
|
2907
|
+
|
|
2908
|
+
if chosen_key is None:
|
|
2909
|
+
return 0
|
|
2910
|
+
if chosen_key == NEW_AISB:
|
|
2911
|
+
return _attach_or_spawn_chat(
|
|
2912
|
+
"AISB-chat",
|
|
2913
|
+
spawn_fn=tmux.spawn_aisb_chat,
|
|
2914
|
+
label="AISB master (Claude Code on Max OAuth)",
|
|
2915
|
+
)
|
|
2916
|
+
if chosen_key == NEW_HERMES:
|
|
2917
|
+
return _attach_or_spawn_chat(
|
|
2918
|
+
"Hermes-chat",
|
|
2919
|
+
spawn_fn=tmux.spawn_hermes_chat,
|
|
2920
|
+
label="Hermès (Claude Code on Anthropic API)",
|
|
2921
|
+
)
|
|
2922
|
+
# Otherwise it's an existing session name — just attach.
|
|
2923
|
+
print(f" attaching to {chosen_key}…")
|
|
2924
|
+
try:
|
|
2925
|
+
os.execvp("tmux", ["tmux", "attach", "-t", chosen_key])
|
|
2926
|
+
except FileNotFoundError:
|
|
2927
|
+
print(" tmux not on PATH")
|
|
2928
|
+
return 2
|
|
2929
|
+
return 0
|
|
2801
2930
|
|
|
2802
2931
|
|
|
2803
2932
|
def cmd_menu_whiptail(_args: argparse.Namespace) -> int:
|
|
@@ -231,42 +231,99 @@ def spawn_worker(project: str, task: str, *,
|
|
|
231
231
|
return name
|
|
232
232
|
|
|
233
233
|
|
|
234
|
+
def _ensure_chat_context_dir(home: Path, label: str,
|
|
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.
|
|
242
|
+
"""
|
|
243
|
+
ctx_dir = home / "Agentik_Coding" / "chat-contexts" / label
|
|
244
|
+
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.
|
|
256
|
+
gi = ctx_dir / ".gitignore"
|
|
257
|
+
if not gi.exists():
|
|
258
|
+
gi.write_text("*\n!.gitignore\n!CLAUDE.md\n")
|
|
259
|
+
return ctx_dir
|
|
260
|
+
|
|
261
|
+
|
|
234
262
|
def spawn_aisb_chat(omega_home: str | Path | None = None) -> str:
|
|
235
|
-
"""Spawn the AISB master chat tmux session.
|
|
263
|
+
"""Spawn the AISB master chat tmux session — REAL Claude Code TUI.
|
|
264
|
+
|
|
265
|
+
v0.19.18 — instead of running our Python REPL (`omega aisb chat-loop`),
|
|
266
|
+
we now run the canonical `claude` CLI inside a dedicated project dir
|
|
267
|
+
seeded with the AISB master persona (``Agentik_SSOT/agents/aisb/CLAUDE.md``).
|
|
268
|
+
User sees the SAME interactive Claude Code TUI they get with bare
|
|
269
|
+
`claude`, but pre-loaded with AISB's identity, the L3 orchestrator
|
|
270
|
+
role, and full OmegaOS context.
|
|
236
271
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
attaches to (v0.19.15+).
|
|
272
|
+
Auth: Claude Max OAuth (inherited from the user's shell env, no env
|
|
273
|
+
override needed — `claude` reads `~/.claude/.credentials.json`).
|
|
240
274
|
|
|
241
|
-
|
|
242
|
-
the
|
|
243
|
-
|
|
244
|
-
subprocess (including `claude -p`) crashed with
|
|
245
|
-
"The current working directory was deleted".
|
|
275
|
+
cwd=$HOME is the legacy crash fix (spawn() default would inherit
|
|
276
|
+
the npm/_npx cache dir which can disappear); the `claude` process
|
|
277
|
+
itself runs with cwd = the context dir we just created.
|
|
246
278
|
"""
|
|
247
279
|
name = "AISB-chat"
|
|
248
280
|
home = Path(omega_home or os.environ.get("OMEGA_HOME")
|
|
249
281
|
or Path.home() / "Omega")
|
|
250
|
-
|
|
251
|
-
|
|
282
|
+
persona = home / "Agentik_SSOT" / "agents" / "aisb" / "CLAUDE.md"
|
|
283
|
+
ctx_dir = _ensure_chat_context_dir(home, "aisb-master", persona)
|
|
284
|
+
spawn(name, command=f"cd {ctx_dir} && exec claude",
|
|
252
285
|
cwd=str(Path.home()))
|
|
253
286
|
return name
|
|
254
287
|
|
|
255
288
|
|
|
256
289
|
def spawn_hermes_chat(omega_home: str | Path | None = None) -> str:
|
|
257
|
-
"""Spawn the Hermès
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
290
|
+
"""Spawn the Hermès chat tmux session — REAL Claude Code TUI, but on
|
|
291
|
+
Anthropic API (Hermès's own paid key, budget-isolated from Max OAuth).
|
|
292
|
+
|
|
293
|
+
Mirrors ``spawn_aisb_chat`` for the L2 companion: drops a Hermès
|
|
294
|
+
persona ``CLAUDE.md`` into a dedicated context dir, then runs
|
|
295
|
+
`claude` there with ``ANTHROPIC_API_KEY`` exported from the vault.
|
|
296
|
+
Setting the env var makes Claude Code use the API key instead of
|
|
297
|
+
the Max OAuth — same TUI, different billing surface.
|
|
263
298
|
"""
|
|
264
299
|
name = "Hermes-chat"
|
|
265
300
|
home = Path(omega_home or os.environ.get("OMEGA_HOME")
|
|
266
301
|
or Path.home() / "Omega")
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
302
|
+
persona = home / "Agentik_SSOT" / "docs" / "LAYERS.md" # Hermès reads architecture as persona
|
|
303
|
+
ctx_dir = _ensure_chat_context_dir(home, "hermes", persona)
|
|
304
|
+
# Resolve Anthropic key from vault → fallback to existing env.
|
|
305
|
+
try:
|
|
306
|
+
from omega_engine.vault import vault_read
|
|
307
|
+
api_key = (vault_read(home, "ANTHROPIC_API_KEY_HERMES") or "").strip()
|
|
308
|
+
except Exception: # noqa: BLE001
|
|
309
|
+
api_key = ""
|
|
310
|
+
if not api_key:
|
|
311
|
+
api_key = (os.environ.get("ANTHROPIC_API_KEY") or "").strip()
|
|
312
|
+
# If no key, fall back to Max OAuth (Hermès will share AISB's
|
|
313
|
+
# billing — not ideal but better than failing). Surface a marker
|
|
314
|
+
# file in the context dir so the user sees the warning on first launch.
|
|
315
|
+
if not api_key:
|
|
316
|
+
(ctx_dir / "NO_ANTHROPIC_KEY.md").write_text(
|
|
317
|
+
"# Hermès — no Anthropic API key wired\n\n"
|
|
318
|
+
"Set `ANTHROPIC_API_KEY_HERMES` in the vault to use Hermès's\n"
|
|
319
|
+
"own paid Anthropic budget instead of the AISB Max OAuth.\n\n"
|
|
320
|
+
" omega vault write ANTHROPIC_API_KEY_HERMES sk-ant-...\n"
|
|
321
|
+
)
|
|
322
|
+
cmd = f"cd {ctx_dir} && exec claude"
|
|
323
|
+
else:
|
|
324
|
+
cmd = (f"cd {ctx_dir} && "
|
|
325
|
+
f"ANTHROPIC_API_KEY={api_key} exec claude")
|
|
326
|
+
spawn(name, command=cmd, cwd=str(Path.home()))
|
|
270
327
|
return name
|
|
271
328
|
|
|
272
329
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.19.
|
|
1
|
+
0.19.19
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentikos/omega-os",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.19",
|
|
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"
|