@agentikos/omega-os 0.19.39 → 0.19.41
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/common.sh +19 -10
- package/bootstrap/templates/aisb/architect.md +27 -1
- package/bootstrap/templates/aisb/construct.md +27 -1
- package/bootstrap/templates/aisb/keymaker.md +27 -1
- package/bootstrap/templates/aisb/link.md +27 -1
- package/bootstrap/templates/aisb/lmc-protocol.md +27 -1
- package/bootstrap/templates/aisb/merovingian.md +27 -1
- package/bootstrap/templates/aisb/morpheus.md +27 -1
- package/bootstrap/templates/aisb/neo.md +27 -1
- package/bootstrap/templates/aisb/niobe.md +27 -1
- package/bootstrap/templates/aisb/oracle.md +27 -1
- package/bootstrap/templates/aisb/pythia.md +36 -0
- package/bootstrap/templates/aisb/seraph.md +27 -1
- package/bootstrap/templates/aisb/smith.md +27 -1
- package/bootstrap/templates/aisb/zion.md +27 -1
- 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/__pycache__/tui.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +35 -6
- package/omega/Agentik_Engine/omega_engine/tmux.py +45 -26
- package/omega/Agentik_Engine/omega_engine/tui.py +25 -20
- package/omega/Agentik_Engine/pyproject.toml +1 -1
- package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_prompt_audit.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_prompt_audit.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.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_tui_runtime.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_tui_runtime.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/test_install_ux.py +87 -2
- package/omega/Agentik_Engine/tests/test_prompt_audit.py +82 -0
- package/omega/Agentik_Engine/tests/test_tmux_and_aisb_chat.py +94 -0
- package/omega/Agentik_Engine/tests/test_tmux_palette.py +94 -0
- package/omega/Agentik_Engine/tests/test_tui_runtime.py +50 -0
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/omega/Agentik_SSOT/docs/AUDIT-V0.19.40.md +163 -0
- package/package.json +1 -1
|
@@ -2989,14 +2989,43 @@ def cmd_menu(_args: argparse.Namespace) -> int:
|
|
|
2989
2989
|
return 2
|
|
2990
2990
|
|
|
2991
2991
|
if os.environ.get("TMUX"):
|
|
2992
|
-
# We're already
|
|
2993
|
-
#
|
|
2992
|
+
# We're already inside a tmux client. Nested `tmux attach` is
|
|
2993
|
+
# rejected by tmux, but `tmux switch-client` works fine — and is
|
|
2994
|
+
# what the user wants when they type `omega` from inside tmux.
|
|
2995
|
+
# v0.19.41 — actually DO the switch instead of just printing
|
|
2996
|
+
# instructions. Print is the v0.19.40 bug ("ça lance rien du tout").
|
|
2997
|
+
import subprocess
|
|
2994
2998
|
if not tmux.is_alive(OMEGA_SESSION):
|
|
2995
2999
|
tmux.spawn_omega_master(_omega_home())
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
+
# Are we already INSIDE the Omega session?
|
|
3001
|
+
proc = subprocess.run(
|
|
3002
|
+
["tmux", "display-message", "-p", "#S"],
|
|
3003
|
+
capture_output=True, text=True,
|
|
3004
|
+
)
|
|
3005
|
+
current = (proc.stdout or "").strip()
|
|
3006
|
+
if current == OMEGA_SESSION:
|
|
3007
|
+
# Already in Omega — just jump to the menu window. If the
|
|
3008
|
+
# menu window was killed, recreate it.
|
|
3009
|
+
wins = subprocess.run(
|
|
3010
|
+
["tmux", "list-windows", "-t", OMEGA_SESSION, "-F", "#W"],
|
|
3011
|
+
capture_output=True, text=True,
|
|
3012
|
+
)
|
|
3013
|
+
if "menu" not in (wins.stdout or "").split():
|
|
3014
|
+
subprocess.run(
|
|
3015
|
+
["tmux", "new-window", "-t", OMEGA_SESSION, "-n",
|
|
3016
|
+
"menu", "omega menu-tui"],
|
|
3017
|
+
capture_output=True,
|
|
3018
|
+
)
|
|
3019
|
+
subprocess.run(
|
|
3020
|
+
["tmux", "select-window", "-t", f"{OMEGA_SESSION}:menu"],
|
|
3021
|
+
capture_output=True,
|
|
3022
|
+
)
|
|
3023
|
+
else:
|
|
3024
|
+
# Different session — switch the client to Omega.
|
|
3025
|
+
subprocess.run(
|
|
3026
|
+
["tmux", "switch-client", "-t", OMEGA_SESSION],
|
|
3027
|
+
capture_output=True,
|
|
3028
|
+
)
|
|
3000
3029
|
return 0
|
|
3001
3030
|
|
|
3002
3031
|
if not tmux.is_alive(OMEGA_SESSION):
|
|
@@ -583,7 +583,7 @@ set -s escape-time 10
|
|
|
583
583
|
# session (AISB / oracle / worker / dev / linear / home / other) without
|
|
584
584
|
# leaving tmux.
|
|
585
585
|
# ════════════════════════════════════════════════════════════════════
|
|
586
|
-
bind-key Z display-popup -E -w
|
|
586
|
+
bind-key Z display-popup -E -w 100% -h 100% "omega tmux menu"
|
|
587
587
|
|
|
588
588
|
# Prefix+S — native session list (tmux's built-in choose-tree)
|
|
589
589
|
bind-key S choose-tree -Zs
|
|
@@ -591,18 +591,28 @@ bind-key S choose-tree -Zs
|
|
|
591
591
|
# Option+/ → session switcher (fzf popup, pick any live tmux session)
|
|
592
592
|
# Option+z → Omega action menu (spawn Omega if missing + switch-client)
|
|
593
593
|
# macOS Option key → enable "Use Option as Meta" in your terminal.
|
|
594
|
-
bind-key -n M-/ display-popup -E -
|
|
594
|
+
bind-key -n M-/ display-popup -E -w 100% -h 100% "omega tmux switcher"
|
|
595
595
|
bind-key -n M-z run-shell -b "tmux has-session -t Omega 2>/dev/null || tmux new-session -d -s Omega -n menu -c $HOME 'omega menu-tui'; tmux switch-client -t Omega"
|
|
596
596
|
|
|
597
597
|
# ════════════════════════════════════════════════════════════════════
|
|
598
|
-
#
|
|
598
|
+
# Pixel/CRT amber theme (tmux-claude inspired)
|
|
599
|
+
# xterm colour137 ≈ #af8700 — amber/gold on whatever your terminal bg is.
|
|
600
|
+
# Theme-neutral: inherits terminal background, works in light + dark.
|
|
599
601
|
# ════════════════════════════════════════════════════════════════════
|
|
600
|
-
set -g status-style "
|
|
601
|
-
set -g status-left
|
|
602
|
-
set -g status-right
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
602
|
+
set -g status-style "fg=default"
|
|
603
|
+
set -g status-left ' #[fg=colour137,bold]Ω #S #[fg=default,nobold]• #[fg=colour137]#(omega tmux count 2>/dev/null) sess #[fg=default]• #[fg=colour137]%H:%M '
|
|
604
|
+
set -g status-right '#[fg=default]TS #[fg=colour137]#(omega tmux count 2>/dev/null) #[fg=default]• #[fg=colour137]Ω '
|
|
605
|
+
|
|
606
|
+
# Hide the window list (tmux-claude clean look).
|
|
607
|
+
setw -g window-status-format ''
|
|
608
|
+
setw -g window-status-current-format ''
|
|
609
|
+
setw -g window-status-separator ''
|
|
610
|
+
|
|
611
|
+
# Pane borders — amber on active, inherit on inactive.
|
|
612
|
+
set -g pane-border-style "fg=default"
|
|
613
|
+
set -g pane-active-border-style "fg=colour137"
|
|
614
|
+
set -g message-style "fg=colour137,bg=default,bold"
|
|
615
|
+
set -g mode-style "fg=default,bg=colour137"
|
|
606
616
|
"""
|
|
607
617
|
|
|
608
618
|
|
|
@@ -681,7 +691,7 @@ set -g @scroll-without-changing-pane on
|
|
|
681
691
|
# Prefix+Z — OmegaOS session picker
|
|
682
692
|
# Opens a tmux popup with `omega tmux menu`.
|
|
683
693
|
# ════════════════════════════════════════════════════════════════════
|
|
684
|
-
bind-key Z display-popup -E -w
|
|
694
|
+
bind-key Z display-popup -E -w 100% -h 100% "omega tmux menu"
|
|
685
695
|
|
|
686
696
|
# Prefix+S — native choose-tree
|
|
687
697
|
bind-key S choose-tree -Zs
|
|
@@ -709,30 +719,39 @@ bind-key r source-file ~/.tmux.conf \\; display-message "tmux.conf reloaded"
|
|
|
709
719
|
# ────────────────────────────────────────────────────────────────────
|
|
710
720
|
# popup -E runs the command in a tmux popup overlay (modal). The popup
|
|
711
721
|
# closes when the command exits, returning the user to their previous
|
|
712
|
-
# pane. -
|
|
713
|
-
bind-key -n M-/ display-popup -E -
|
|
722
|
+
# pane. -w 100% -h 100% gives a full-screen popup (tmux-claude style).
|
|
723
|
+
bind-key -n M-/ display-popup -E -w 100% -h 100% "omega tmux switcher"
|
|
714
724
|
bind-key -n M-z run-shell -b "tmux has-session -t Omega 2>/dev/null || tmux new-session -d -s Omega -n menu -c $HOME 'omega menu-tui'; tmux switch-client -t Omega"
|
|
715
725
|
|
|
716
726
|
# ════════════════════════════════════════════════════════════════════
|
|
717
|
-
#
|
|
727
|
+
# Pixel/CRT amber theme (tmux-claude inspired)
|
|
718
728
|
# ════════════════════════════════════════════════════════════════════
|
|
719
|
-
#
|
|
720
|
-
#
|
|
721
|
-
#
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
729
|
+
# xterm colour137 ≈ #af8700 — amber/gold. Theme-neutral: status bar
|
|
730
|
+
# inherits the terminal background, so it looks right in any color
|
|
731
|
+
# scheme (dark, light, solarized, etc.).
|
|
732
|
+
#
|
|
733
|
+
# Bullets `•` everywhere as separators, accent colour137 for values,
|
|
734
|
+
# default fg for labels. Window list HIDDEN (tmux-claude clean look) —
|
|
735
|
+
# the operator switches via Option+/ or `omega tmux menu` instead.
|
|
736
|
+
set -g status-style "fg=default"
|
|
737
|
+
set -g status-left ' #[fg=colour137,bold]Ω #S #[fg=default,nobold]• #[fg=colour137]#(omega tmux count 2>/dev/null) sess #[fg=default]• #[fg=colour137]%H:%M '
|
|
738
|
+
set -g status-right '#[fg=default]CPU #[fg=colour137]#(awk "{print int(\\$1*100/$(nproc))}" /proc/loadavg 2>/dev/null || echo --)% #[fg=default]• #[fg=colour137]Ω v#(cat $OMEGA_HOME/Agentik_SSOT/VERSION 2>/dev/null || echo dev) '
|
|
725
739
|
set -g status-interval 5
|
|
726
|
-
set -g status-left-length
|
|
740
|
+
set -g status-left-length 60
|
|
727
741
|
set -g status-right-length 60
|
|
728
742
|
|
|
729
|
-
#
|
|
730
|
-
|
|
731
|
-
|
|
743
|
+
# Hide the window list (tmux-claude clean look).
|
|
744
|
+
setw -g window-status-format ''
|
|
745
|
+
setw -g window-status-current-format ''
|
|
746
|
+
setw -g window-status-separator ''
|
|
747
|
+
|
|
748
|
+
# Pane borders — amber on active, inherit on inactive.
|
|
749
|
+
set -g pane-border-style "fg=default"
|
|
750
|
+
set -g pane-active-border-style "fg=colour137"
|
|
732
751
|
|
|
733
|
-
# Message + copy-mode prompts —
|
|
734
|
-
set -g message-style "bg
|
|
735
|
-
set -g mode-style "bg
|
|
752
|
+
# Message + copy-mode prompts — amber on terminal bg.
|
|
753
|
+
set -g message-style "fg=colour137,bg=default,bold"
|
|
754
|
+
set -g mode-style "fg=default,bg=colour137"
|
|
736
755
|
"""
|
|
737
756
|
|
|
738
757
|
|
|
@@ -441,11 +441,16 @@ def _arrow_menu() -> int:
|
|
|
441
441
|
OMEGA_BIN = str(HOME / "Agentik_Tools" / "bin" / "omega")
|
|
442
442
|
|
|
443
443
|
# ANSI escapes — used via fzf --ansi.
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
444
|
+
# Pixel/CRT amber palette — colour137 family.
|
|
445
|
+
# Inherits terminal background (no cream); accent = amber gold.
|
|
446
|
+
AMBER = "\033[38;5;137m" # xterm 256 colour137 = amber gold
|
|
447
|
+
AMBER_BR = "\033[38;5;215m" # brighter amber for active/highlighted rows
|
|
448
|
+
MUTED = "\033[38;5;240m" # dim gray
|
|
449
|
+
BOLD = "\033[1m"
|
|
450
|
+
DIM = "\033[2m"
|
|
451
|
+
RST = "\033[0m"
|
|
452
|
+
# Backward-compat alias — existing code may reference ORANGE.
|
|
453
|
+
ORANGE = AMBER
|
|
449
454
|
|
|
450
455
|
def _label(name: str, hint: str = "") -> str:
|
|
451
456
|
"""Two-column label: name on the left, hint dimmed on the right."""
|
|
@@ -469,7 +474,7 @@ def _arrow_menu() -> int:
|
|
|
469
474
|
# a short "MENU" section. Reasoning: the user spends 99% of their
|
|
470
475
|
# time in conversations — the menu should reflect that, not bury the
|
|
471
476
|
# chats below 6 sections of admin options.
|
|
472
|
-
DOT_ON = f"{
|
|
477
|
+
DOT_ON = f"{AMBER}●{RST}" # alive
|
|
473
478
|
DOT_OFF = f"{MUTED}○{RST}" # not running
|
|
474
479
|
|
|
475
480
|
def _dot(alive: bool) -> str:
|
|
@@ -700,20 +705,20 @@ def _arrow_menu() -> int:
|
|
|
700
705
|
"--pointer=▶",
|
|
701
706
|
"--marker=●",
|
|
702
707
|
"--color="
|
|
703
|
-
"bg
|
|
704
|
-
"fg
|
|
705
|
-
"bg+:#
|
|
706
|
-
"fg
|
|
707
|
-
"hl
|
|
708
|
-
"hl
|
|
709
|
-
"prompt
|
|
710
|
-
"pointer
|
|
711
|
-
"marker
|
|
712
|
-
"header
|
|
713
|
-
"border
|
|
714
|
-
"info
|
|
715
|
-
"spinner
|
|
716
|
-
"gutter
|
|
708
|
+
"bg:-1,"
|
|
709
|
+
"fg:default,"
|
|
710
|
+
"bg+:#1c1c1c,"
|
|
711
|
+
"fg+:colour215,"
|
|
712
|
+
"hl:colour137,"
|
|
713
|
+
"hl+:colour215,"
|
|
714
|
+
"prompt:colour137,"
|
|
715
|
+
"pointer:colour137,"
|
|
716
|
+
"marker:colour215,"
|
|
717
|
+
"header:colour137,"
|
|
718
|
+
"border:colour137,"
|
|
719
|
+
"info:colour137,"
|
|
720
|
+
"spinner:colour137,"
|
|
721
|
+
"gutter:-1"],
|
|
717
722
|
input="\n".join(lines), capture_output=True, text=True,
|
|
718
723
|
)
|
|
719
724
|
except (KeyboardInterrupt, subprocess.SubprocessError):
|
|
Binary file
|
|
Binary file
|
package/omega/Agentik_Engine/tests/__pycache__/test_prompt_audit.cpython-313-pytest-8.4.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
package/omega/Agentik_Engine/tests/__pycache__/test_tmux_and_aisb_chat.cpython-313-pytest-8.4.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/omega/Agentik_Engine/tests/__pycache__/test_tui_runtime.cpython-313-pytest-8.4.2.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -18,8 +18,14 @@ COMMON_SH = REPO_ROOT / "bootstrap" / "lib" / "common.sh"
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _bash(snippet: str, *, env: dict[str, str] | None = None,
|
|
21
|
-
stdin: str = "") -> tuple[int, str, str]:
|
|
22
|
-
"""Run a bash snippet that sources common.sh; return (rc, stdout, stderr).
|
|
21
|
+
stdin: str = "", raw: bool = False) -> tuple[int, str, str]:
|
|
22
|
+
"""Run a bash snippet that sources common.sh; return (rc, stdout, stderr).
|
|
23
|
+
|
|
24
|
+
raw=True disables Python's universal-newlines translation so callers can
|
|
25
|
+
distinguish CR (\\r — progress-bar overwrite) from LF (\\n — newline).
|
|
26
|
+
Without it, `text=True` collapses both into "\\n" and tests that count
|
|
27
|
+
newlines lose the signal they need.
|
|
28
|
+
"""
|
|
23
29
|
base_env = {**os.environ}
|
|
24
30
|
base_env.setdefault("OMEGA_REPO", str(REPO_ROOT))
|
|
25
31
|
base_env.setdefault("OMEGA_HOME", "/tmp/omega-ux-pytest")
|
|
@@ -40,6 +46,14 @@ def _bash(snippet: str, *, env: dict[str, str] | None = None,
|
|
|
40
46
|
"C_BOLD=''; C_CYAN=''; C_GREEN=''; C_YELLOW=''; C_RED=''; C_MAGENTA=''; C_BLUE=''\n"
|
|
41
47
|
+ snippet
|
|
42
48
|
)
|
|
49
|
+
if raw:
|
|
50
|
+
proc = subprocess.run(
|
|
51
|
+
["bash", "-c", code],
|
|
52
|
+
env=base_env, capture_output=True, input=stdin.encode("utf-8"),
|
|
53
|
+
timeout=15,
|
|
54
|
+
)
|
|
55
|
+
return proc.returncode, proc.stdout.decode("utf-8", "replace"), \
|
|
56
|
+
proc.stderr.decode("utf-8", "replace")
|
|
43
57
|
proc = subprocess.run(
|
|
44
58
|
["bash", "-c", code],
|
|
45
59
|
env=base_env, capture_output=True, text=True, input=stdin,
|
|
@@ -131,6 +145,77 @@ class TestPathSetup(unittest.TestCase):
|
|
|
131
145
|
self.assertEqual(rc_path.read_text(), "# only this\n")
|
|
132
146
|
|
|
133
147
|
|
|
148
|
+
class TestProgressBarCountingDiscipline(unittest.TestCase):
|
|
149
|
+
"""Regression tests for the v0.19.x progress-bar double-count bug.
|
|
150
|
+
|
|
151
|
+
`run_step` calls `_full_progress` TWICE per step ("run" then "ok"). The
|
|
152
|
+
old implementation incremented STEP_COUNT on EVERY call, which with
|
|
153
|
+
STEP_TOTAL=21 drove the counter to 42, the bar to 200%, and the "current
|
|
154
|
+
>= total → newline" branch fired four+ times — so the user saw the
|
|
155
|
+
progress bar spawn fresh lines mid-install instead of overwriting one
|
|
156
|
+
single line. The fix: only "ok"/"skip" count as real progress; "run"
|
|
157
|
+
just re-paints the bar with the in-progress step name.
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
def test_full_progress_counts_only_ok_and_skip(self):
|
|
161
|
+
rc, out, _ = _bash(
|
|
162
|
+
"export STEP_TOTAL=10\n"
|
|
163
|
+
"_full_progress a run\n"
|
|
164
|
+
"_full_progress a ok\n"
|
|
165
|
+
"_full_progress b run\n"
|
|
166
|
+
"_full_progress b skip\n"
|
|
167
|
+
"echo STEP_COUNT=$STEP_COUNT\n"
|
|
168
|
+
)
|
|
169
|
+
self.assertEqual(rc, 0)
|
|
170
|
+
# Two runs + one ok + one skip → count should be 2 (only ok+skip count).
|
|
171
|
+
self.assertIn("STEP_COUNT=2", out)
|
|
172
|
+
|
|
173
|
+
def test_no_premature_newline_in_middle_of_run(self):
|
|
174
|
+
# STEP_TOTAL=2 so the second "ok" hits the boundary; nothing before
|
|
175
|
+
# that may emit a newline. With the old code, the boundary fired on
|
|
176
|
+
# the very FIRST "ok" (count had ticked to 2 already because run+ok
|
|
177
|
+
# = +2 instead of +1), producing a stray mid-install newline.
|
|
178
|
+
# raw=True so Python doesn't fold \r into \n via universal-newlines.
|
|
179
|
+
rc, out, _ = _bash(
|
|
180
|
+
"export STEP_TOTAL=2\n"
|
|
181
|
+
"_full_progress a run\n"
|
|
182
|
+
"_full_progress a ok\n"
|
|
183
|
+
"_full_progress b run\n"
|
|
184
|
+
"_full_progress b ok\n",
|
|
185
|
+
raw=True,
|
|
186
|
+
)
|
|
187
|
+
self.assertEqual(rc, 0)
|
|
188
|
+
# Frames are separated by \r (carriage return → overwrite the line),
|
|
189
|
+
# NOT \n. Exactly one \n — emitted ONLY after the final "ok" crosses total.
|
|
190
|
+
self.assertEqual(out.count("\n"), 1,
|
|
191
|
+
f"expected 1 trailing newline, got {out.count(chr(10))}: {out!r}")
|
|
192
|
+
# Carriage returns prove the frames overwrote rather than scrolled.
|
|
193
|
+
self.assertGreaterEqual(out.count("\r"), 3)
|
|
194
|
+
# Final frame must read 2/2 100% — not 3/2 150% or 4/2 200% like the bug.
|
|
195
|
+
self.assertIn("2/2", out)
|
|
196
|
+
self.assertIn("100%", out)
|
|
197
|
+
self.assertNotIn("3/2", out)
|
|
198
|
+
self.assertNotIn("4/2", out)
|
|
199
|
+
self.assertNotIn("150%", out)
|
|
200
|
+
self.assertNotIn("200%", out)
|
|
201
|
+
|
|
202
|
+
def test_run_phase_shows_in_progress_step_name(self):
|
|
203
|
+
# The "run" phase should re-paint with the step name being attempted
|
|
204
|
+
# so the user sees what's currently happening, even though the count
|
|
205
|
+
# hasn't bumped yet.
|
|
206
|
+
rc, out, _ = _bash(
|
|
207
|
+
"export STEP_TOTAL=5\n"
|
|
208
|
+
"_full_progress mystep run\n",
|
|
209
|
+
raw=True,
|
|
210
|
+
)
|
|
211
|
+
self.assertEqual(rc, 0)
|
|
212
|
+
self.assertIn("mystep", out)
|
|
213
|
+
# Count is still 0 because nothing has finished yet.
|
|
214
|
+
self.assertIn("0/5", out)
|
|
215
|
+
# And critically — no newline during a "run" frame.
|
|
216
|
+
self.assertEqual(out.count("\n"), 0)
|
|
217
|
+
|
|
218
|
+
|
|
134
219
|
class TestPostInstallCard(unittest.TestCase):
|
|
135
220
|
def test_card_includes_status_and_next_steps(self):
|
|
136
221
|
# omega CLI isn't on PATH inside the test sandbox → readiness=UNKNOWN
|
|
@@ -195,5 +195,87 @@ class TestAuditAisbSuiteAgainstRealRepo(unittest.TestCase):
|
|
|
195
195
|
self.assertLessEqual(overlap, 1.0)
|
|
196
196
|
|
|
197
197
|
|
|
198
|
+
class TestEnrichedRolePromptsRegression(unittest.TestCase):
|
|
199
|
+
"""v0.19.40 — every AISB role prompt was enriched with a uniform
|
|
200
|
+
`THREE LAWS + Operating Contract` block (Three Laws + LMC protocol
|
|
201
|
+
ref + .done.json contract + done-marker + fresh-context handoff).
|
|
202
|
+
The audit went from 52.0/100 average → 98.3/100. These tests lock in
|
|
203
|
+
that quality floor so a future template edit can't silently drop a
|
|
204
|
+
role below the contract."""
|
|
205
|
+
|
|
206
|
+
def test_every_aisb_role_scores_at_least_80(self):
|
|
207
|
+
if not TEMPLATES.is_dir():
|
|
208
|
+
self.skipTest("AISB templates not present in repo")
|
|
209
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
210
|
+
home = _seed_real_aisb(Path(tmp))
|
|
211
|
+
report = audit_aisb_suite(home)
|
|
212
|
+
below = [(r.agent_id, r.score) for r in report.per_agent if r.score < 80]
|
|
213
|
+
self.assertEqual(below, [],
|
|
214
|
+
f"v0.19.40 contract requires every role ≥80/100. "
|
|
215
|
+
f"Regression: {below} — re-enrich the failing role with "
|
|
216
|
+
f"the THREE LAWS + Operating Contract block.")
|
|
217
|
+
|
|
218
|
+
def test_average_suite_score_at_least_85(self):
|
|
219
|
+
if not TEMPLATES.is_dir():
|
|
220
|
+
self.skipTest("AISB templates not present in repo")
|
|
221
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
222
|
+
home = _seed_real_aisb(Path(tmp))
|
|
223
|
+
report = audit_aisb_suite(home)
|
|
224
|
+
self.assertGreaterEqual(
|
|
225
|
+
report.average_score, 85.0,
|
|
226
|
+
f"v0.19.40 baseline: 98.3 average. Floor: 85. "
|
|
227
|
+
f"Got {report.average_score:.1f} — investigate which roles "
|
|
228
|
+
f"lost contract references.")
|
|
229
|
+
|
|
230
|
+
def test_done_json_vocab_overlap_at_least_80_percent(self):
|
|
231
|
+
"""Before v0.19.40 only 33% of roles referenced `.done.json`
|
|
232
|
+
in their on-disk file (the rest relied on the loader to concat
|
|
233
|
+
lmc-protocol.md at spawn). The enrichment brought it to 100%."""
|
|
234
|
+
if not TEMPLATES.is_dir():
|
|
235
|
+
self.skipTest("AISB templates not present in repo")
|
|
236
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
237
|
+
home = _seed_real_aisb(Path(tmp))
|
|
238
|
+
oh = orchestration_health(home)
|
|
239
|
+
self.assertGreaterEqual(
|
|
240
|
+
oh["shared_vocab_overlap"], 0.80,
|
|
241
|
+
f"`.done.json` vocab overlap must stay ≥80% — "
|
|
242
|
+
f"got {oh['shared_vocab_overlap']*100:.0f}%. "
|
|
243
|
+
f"A role file lost its done.json reference.")
|
|
244
|
+
|
|
245
|
+
def test_every_role_references_lmc_protocol(self):
|
|
246
|
+
"""The LMC protocol must be named in every role file (not just
|
|
247
|
+
implicit via the loader's concat). Direct grep check."""
|
|
248
|
+
if not TEMPLATES.is_dir():
|
|
249
|
+
self.skipTest("AISB templates not present in repo")
|
|
250
|
+
import re as _re
|
|
251
|
+
lmc_re = _re.compile(
|
|
252
|
+
r"(lmc[\s\-_]*(protocol|gate)?|lead[\s\-]+manager[\s\-]+checker|"
|
|
253
|
+
r"lmc-protocol\.md)",
|
|
254
|
+
_re.IGNORECASE,
|
|
255
|
+
)
|
|
256
|
+
missing = []
|
|
257
|
+
for md in sorted(TEMPLATES.glob("*.md")):
|
|
258
|
+
if not lmc_re.search(md.read_text()):
|
|
259
|
+
missing.append(md.name)
|
|
260
|
+
self.assertEqual(missing, [],
|
|
261
|
+
f"Every AISB role must reference the LMC protocol — "
|
|
262
|
+
f"missing in: {missing}")
|
|
263
|
+
|
|
264
|
+
def test_every_role_references_three_laws(self):
|
|
265
|
+
"""Three Laws must be present (not just two). Pre-v0.19.40
|
|
266
|
+
most roles only had LAW 1 + LAW 2. Now LAW 3 is mandatory."""
|
|
267
|
+
if not TEMPLATES.is_dir():
|
|
268
|
+
self.skipTest("AISB templates not present in repo")
|
|
269
|
+
missing_third = []
|
|
270
|
+
for md in sorted(TEMPLATES.glob("*.md")):
|
|
271
|
+
text = md.read_text()
|
|
272
|
+
# Match LAW 3 or 'Third Law' or 'law 3' — same regex as the audit.
|
|
273
|
+
if "LAW 3" not in text and "Third Law" not in text.lower():
|
|
274
|
+
missing_third.append(md.name)
|
|
275
|
+
self.assertEqual(missing_third, [],
|
|
276
|
+
f"Every AISB role must reference LAW 3 (autonomous execution). "
|
|
277
|
+
f"Missing in: {missing_third}")
|
|
278
|
+
|
|
279
|
+
|
|
198
280
|
if __name__ == "__main__":
|
|
199
281
|
unittest.main()
|
|
@@ -219,5 +219,99 @@ class TestAisbChat(unittest.TestCase):
|
|
|
219
219
|
self.assertEqual(history[1].text, "ok, will look into it")
|
|
220
220
|
|
|
221
221
|
|
|
222
|
+
class TestBareOmegaAttachesNotPrints(unittest.TestCase):
|
|
223
|
+
"""v0.19.41 — bug fix. `omega` (no args) used to PRINT instructions
|
|
224
|
+
instead of doing the switch when invoked from inside tmux. Lock in
|
|
225
|
+
that cmd_menu calls tmux switch-client / select-window directly."""
|
|
226
|
+
|
|
227
|
+
def test_cmd_menu_when_in_tmux_calls_switch_client(self):
|
|
228
|
+
"""When $TMUX is set AND we're in a DIFFERENT session than Omega,
|
|
229
|
+
cmd_menu must run `tmux switch-client -t Omega` itself — not
|
|
230
|
+
print a hint and exit."""
|
|
231
|
+
import argparse
|
|
232
|
+
with mock.patch.dict("os.environ", {"TMUX": "/tmp/tmux-fake,1,0"}):
|
|
233
|
+
calls = []
|
|
234
|
+
class _FakeProc:
|
|
235
|
+
stdout = "some-other-session\n"
|
|
236
|
+
returncode = 0
|
|
237
|
+
def _fake_run(cmd, **kw):
|
|
238
|
+
calls.append(cmd)
|
|
239
|
+
return _FakeProc()
|
|
240
|
+
with mock.patch("subprocess.run", side_effect=_fake_run), \
|
|
241
|
+
mock.patch("shutil.which", return_value="/usr/bin/tmux"), \
|
|
242
|
+
mock.patch("omega_engine.tmux.is_alive", return_value=True):
|
|
243
|
+
from omega_engine.cli import cmd_menu
|
|
244
|
+
rc = cmd_menu(argparse.Namespace())
|
|
245
|
+
self.assertEqual(rc, 0)
|
|
246
|
+
switch_calls = [c for c in calls
|
|
247
|
+
if isinstance(c, list)
|
|
248
|
+
and len(c) >= 4
|
|
249
|
+
and c[:2] == ["tmux", "switch-client"]]
|
|
250
|
+
self.assertTrue(switch_calls,
|
|
251
|
+
f"cmd_menu must call `tmux switch-client -t Omega` when "
|
|
252
|
+
f"invoked from inside a different tmux session. "
|
|
253
|
+
f"Captured calls: {calls}")
|
|
254
|
+
|
|
255
|
+
def test_cmd_menu_when_already_in_omega_selects_menu_window(self):
|
|
256
|
+
"""If the user is ALREADY in the Omega session, cmd_menu must
|
|
257
|
+
select the :menu window (not switch-client — that'd be a no-op)."""
|
|
258
|
+
import argparse
|
|
259
|
+
with mock.patch.dict("os.environ", {"TMUX": "/tmp/tmux-fake,1,0"}):
|
|
260
|
+
calls = []
|
|
261
|
+
class _ProcS:
|
|
262
|
+
stdout = "Omega\n"; returncode = 0
|
|
263
|
+
class _ProcWins:
|
|
264
|
+
stdout = "menu\naisb\nhermes\n"; returncode = 0
|
|
265
|
+
def _fake_run(cmd, **kw):
|
|
266
|
+
calls.append(cmd)
|
|
267
|
+
if cmd[:3] == ["tmux", "display-message", "-p"]:
|
|
268
|
+
return _ProcS()
|
|
269
|
+
if cmd[:3] == ["tmux", "list-windows", "-t"]:
|
|
270
|
+
return _ProcWins()
|
|
271
|
+
class R: returncode = 0; stdout = ""
|
|
272
|
+
return R()
|
|
273
|
+
with mock.patch("subprocess.run", side_effect=_fake_run), \
|
|
274
|
+
mock.patch("shutil.which", return_value="/usr/bin/tmux"), \
|
|
275
|
+
mock.patch("omega_engine.tmux.is_alive", return_value=True):
|
|
276
|
+
from omega_engine.cli import cmd_menu
|
|
277
|
+
rc = cmd_menu(argparse.Namespace())
|
|
278
|
+
self.assertEqual(rc, 0)
|
|
279
|
+
select_calls = [c for c in calls
|
|
280
|
+
if isinstance(c, list)
|
|
281
|
+
and len(c) >= 4
|
|
282
|
+
and c[:2] == ["tmux", "select-window"]
|
|
283
|
+
and "Omega:menu" in c]
|
|
284
|
+
self.assertTrue(select_calls,
|
|
285
|
+
f"cmd_menu must call `tmux select-window -t Omega:menu` "
|
|
286
|
+
f"when already inside Omega. Captured: {calls}")
|
|
287
|
+
switch_calls = [c for c in calls
|
|
288
|
+
if isinstance(c, list)
|
|
289
|
+
and len(c) >= 2
|
|
290
|
+
and c[:2] == ["tmux", "switch-client"]]
|
|
291
|
+
self.assertEqual(switch_calls, [],
|
|
292
|
+
f"cmd_menu must NOT switch-client when already in Omega.")
|
|
293
|
+
|
|
294
|
+
def test_cmd_menu_does_not_print_hint_when_in_tmux(self):
|
|
295
|
+
"""REGRESSION — the v0.19.40 bug: cmd_menu printed
|
|
296
|
+
'Omega session is running. Switch to it with: ...' instead of
|
|
297
|
+
actually switching."""
|
|
298
|
+
import argparse, io, sys as _sys
|
|
299
|
+
with mock.patch.dict("os.environ", {"TMUX": "/tmp/tmux-fake,1,0"}):
|
|
300
|
+
class _Proc:
|
|
301
|
+
stdout = "Other\n"; returncode = 0
|
|
302
|
+
with mock.patch("subprocess.run", return_value=_Proc()), \
|
|
303
|
+
mock.patch("shutil.which", return_value="/usr/bin/tmux"), \
|
|
304
|
+
mock.patch("omega_engine.tmux.is_alive", return_value=True), \
|
|
305
|
+
mock.patch.object(_sys, "stdout", new_callable=io.StringIO) as captured:
|
|
306
|
+
from omega_engine.cli import cmd_menu
|
|
307
|
+
cmd_menu(argparse.Namespace())
|
|
308
|
+
out = captured.getvalue()
|
|
309
|
+
self.assertNotIn("Switch to it with", out,
|
|
310
|
+
f"v0.19.40 regression: cmd_menu printed switch instructions "
|
|
311
|
+
f"instead of switching. Output: {out!r}")
|
|
312
|
+
self.assertNotIn("tmux switch-client -t Omega", out,
|
|
313
|
+
"cmd_menu must DO the switch, not print the command")
|
|
314
|
+
|
|
315
|
+
|
|
222
316
|
if __name__ == "__main__":
|
|
223
317
|
unittest.main(verbosity=2)
|