@agentikos/omega-os 0.19.13 → 0.19.15

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.
Files changed (23) hide show
  1. package/bootstrap/lib/common.sh +12 -6
  2. package/bootstrap/lib/steps.sh +84 -21
  3. package/omega/Agentik_Engine/omega_engine/__init__.py +1 -1
  4. package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
  5. package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
  6. package/omega/Agentik_Engine/omega_engine/__pycache__/hermes.cpython-313.pyc +0 -0
  7. package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
  8. package/omega/Agentik_Engine/omega_engine/cli.py +124 -3
  9. package/omega/Agentik_Engine/omega_engine/hermes.py +34 -8
  10. package/omega/Agentik_Engine/omega_engine/tmux.py +19 -4
  11. package/omega/Agentik_Engine/pyproject.toml +1 -1
  12. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313-pytest-8.4.2.pyc +0 -0
  13. package/omega/Agentik_Engine/tests/__pycache__/test_hermes_and_ua.cpython-313.pyc +0 -0
  14. package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313-pytest-8.4.2.pyc +0 -0
  15. package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313.pyc +0 -0
  16. package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313-pytest-8.4.2.pyc +0 -0
  17. package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313.pyc +0 -0
  18. package/omega/Agentik_Engine/tests/test_hermes_and_ua.py +56 -8
  19. package/omega/Agentik_Engine/tests/test_install_ux.py +6 -1
  20. package/omega/Agentik_Engine/tests/test_installer_wiring.py +16 -11
  21. package/omega/Agentik_SSOT/VERSION +1 -1
  22. package/omega/Agentik_SSOT/docs/LAYERS.md +130 -73
  23. package/package.json +1 -1
@@ -550,13 +550,19 @@ except Exception: print("unknown")' 2>/dev/null)" || readiness="unknown"
550
550
  printf ' %s elapsed %s\n' "$bar" "$(printf '%02d:%02d' "$mm" "$ss")"
551
551
  printf ' %s\n' "$bar"
552
552
  printf ' %s %snext steps%s\n' "$bar" "$C_BOLD" "$C_RST"
553
- printf " %s 1. %stmux attach -t AISB-chat%s %salways-on Hermès conversation%s\n" \
553
+ printf " %s 1. ${C_BOLD}source ~/.zshrc${C_RST} ${C_DIM}or open a new shell${C_RST}\n" "$bar"
554
+ printf ' %s 2. %somega%s %stalk to AISB (master agent, Max OAuth)%s\n' \
555
+ "$bar" "$C_BOLD" "$C_RST" "$C_DIM" "$C_RST"
556
+ printf ' %s 3. %somega hermes%s %stalk to Hermès (meta-companion, Anthropic API)%s\n' \
557
+ "$bar" "$C_BOLD" "$C_RST" "$C_DIM" "$C_RST"
558
+ printf ' %s 4. %somega account login%s %swire your Claude Max OAuth%s\n' \
559
+ "$bar" "$C_BOLD" "$C_RST" "$C_DIM" "$C_RST"
560
+ printf ' %s 5. %somega run "hello"%s %sfirst verified mission%s\n' \
561
+ "$bar" "$C_BOLD" "$C_RST" "$C_DIM" "$C_RST"
562
+ printf ' %s 6. %somega menu%s %sinteractive whiptail picker%s\n' \
563
+ "$bar" "$C_BOLD" "$C_RST" "$C_DIM" "$C_RST"
564
+ printf ' %s 7. %somega doctor%s %sfull health check%s\n' \
554
565
  "$bar" "$C_BOLD" "$C_RST" "$C_DIM" "$C_RST"
555
- printf " %s 2. open a new shell ${C_DIM}(or \`source ~/.zshrc\`)${C_RST}\n" "$bar"
556
- printf ' %s 3. %somega status%s every task + derived state\n' "$bar" "$C_BOLD" "$C_RST"
557
- printf ' %s 4. %somega account list%s wire your Claude Max OAuth\n' "$bar" "$C_BOLD" "$C_RST"
558
- printf ' %s 5. %somega run "hello"%s first verified mission\n' "$bar" "$C_BOLD" "$C_RST"
559
- printf ' %s 6. %somega%s interactive menu (whiptail)\n' "$bar" "$C_BOLD" "$C_RST"
560
566
  printf ' %s\n' "$bar"
561
567
  printf ' %s %ssessions%s all Omega/Claude/LLM sessions run inside tmux\n' \
562
568
  "$bar" "$C_BOLD" "$C_RST"
@@ -448,10 +448,11 @@ PY
448
448
  step_mcp() {
449
449
  local catalog="$OMEGA_HOME/Agentik_SSOT/mcp/mcp-catalog.yaml"
450
450
  [ -f "$catalog" ] || { err "MCP catalog missing: $catalog"; return 1; }
451
- # grep -c exits 1 when count is 0, which made the old `|| echo '?'`
452
- # fire AND grep's "0" stayed printed "0\n? entries" splatter.
451
+ # The catalog YAML uses ``catalog:`` then `` - id: <name>`` (2-space
452
+ # indent). The old pattern ``^- id:`` matched at column 0 and counted
453
+ # zero. Allow leading whitespace.
453
454
  local _mcp_count
454
- _mcp_count="$(grep -c '^- id:' "$catalog" 2>/dev/null || true)"
455
+ _mcp_count="$(grep -cE '^[[:space:]]*- id:' "$catalog" 2>/dev/null || true)"
455
456
  _mcp_count="$(printf '%s' "$_mcp_count" | tr -d '\n ' )"
456
457
  info "MCP catalog present (${_mcp_count:-?} entries)"
457
458
 
@@ -865,37 +866,55 @@ _claude_plugins_prompt_checklist() {
865
866
 
866
867
  # --- 50 -----------------------------------------------------------------------
867
868
  step_telegram() {
868
- local token chat_id
869
- ask token "Telegram bot token (from @BotFather)" "${TELEGRAM_TOKEN:-}"
870
- if [ -z "$token" ]; then
871
- err "a Telegram bot token is required (or install with --profile minimal)"
869
+ # Two distinct bots in the corrected v0.19.14 model:
870
+ # * AISB bot (token A) REQUIRED. Runs missions on Max OAuth.
871
+ # * Hermès bot (token H) — OPTIONAL. Meta-companion on Anthropic API.
872
+ # And one optional API key (ANTHROPIC_API_KEY_HERMES) gating Hermès AI.
873
+ # Old `TELEGRAM_TOKEN` is treated as the AISB token (back-compat).
874
+ local aisb_token hermes_token anthropic_key chat_id
875
+
876
+ # 1. AISB bot — required.
877
+ info "AISB bot — required. Talks to OmegaOS on Claude Max OAuth."
878
+ ask aisb_token "AISB Telegram bot token (from @BotFather)" "${TELEGRAM_TOKEN_AISB:-${TELEGRAM_TOKEN:-}}"
879
+ if [ -z "$aisb_token" ]; then
880
+ err "an AISB bot token is required (or install with --profile minimal)"
872
881
  return 1
873
882
  fi
874
883
  if have curl; then
875
- if ! curl -s "https://api.telegram.org/bot${token}/getMe" | grep -q '"ok":true'; then
876
- err "Telegram token rejected by the API"
884
+ if ! curl -s "https://api.telegram.org/bot${aisb_token}/getMe" | grep -q '"ok":true'; then
885
+ err "AISB Telegram token rejected by the API"
877
886
  return 1
878
887
  fi
879
888
  fi
889
+
880
890
  local sec="$OMEGA_HOME/Agentik_Extra/etc/secrets"
881
891
  mkdir -p "$sec"
882
- printf 'TELEGRAM_TOKEN=%s\n' "$token" > "$sec/telegram.env"
892
+ # Write a single .env file consumed by both daemons; vault still holds
893
+ # the canonical encrypted copy.
894
+ {
895
+ echo "# Generated by OmegaOS install — do not edit by hand."
896
+ echo "# AISB bot (OmegaOS-native, Claude Max OAuth)"
897
+ printf 'TELEGRAM_TOKEN_AISB=%s\n' "$aisb_token"
898
+ printf 'TELEGRAM_TOKEN=%s\n' "$aisb_token" # back-compat alias
899
+ } > "$sec/telegram.env"
883
900
  chmod 600 "$sec/telegram.env"
884
- # Also write the token into the encrypted vault for the new code path.
885
- python3 - "$OMEGA_HOME" "$token" <<'PY' || true
901
+ python3 - "$OMEGA_HOME" "$aisb_token" <<'PY' || true
886
902
  import sys
887
903
  from pathlib import Path
888
904
  home = Path(sys.argv[1])
889
905
  sys.path.insert(0, str(home / "Agentik_Engine"))
890
906
  try:
891
907
  from omega_engine.vault import vault_write
908
+ # Write under BOTH names so old code paths reading TELEGRAM_TOKEN keep
909
+ # working without a separate migration step.
910
+ vault_write(home, "TELEGRAM_TOKEN_AISB", sys.argv[2])
892
911
  vault_write(home, "TELEGRAM_TOKEN", sys.argv[2])
893
912
  except Exception as exc:
894
913
  print(f"(vault write skipped: {exc})")
895
914
  PY
896
- ok "Telegram bot validated and wired"
915
+ ok "AISB Telegram bot validated and wired"
897
916
 
898
- # Capture the forum group chat id — manifest first, then prompt.
917
+ # 2. AISB group chat id — same as before (scopes missions to a group).
899
918
  chat_id="$(python3 - "$MANIFEST" <<'PY' 2>/dev/null || true
900
919
  import sys, yaml
901
920
  try:
@@ -906,14 +925,11 @@ except Exception:
906
925
  print('')
907
926
  PY
908
927
  )"
909
- chat_id="${chat_id//$'\n'/}"
928
+ chat_id="$(printf '%s' "$chat_id" | tr -d '\n ')"
910
929
  if [ -z "$chat_id" ]; then
911
- ask chat_id "Telegram forum group chat id (e.g. -1001234567890; blank to set later)" ""
930
+ ask chat_id "AISB forum group chat id (e.g. -1001234567890; blank to set later)" ""
912
931
  fi
913
932
  if [ -n "$chat_id" ]; then
914
- python3 "$OMEGA_REPO/bootstrap/lib/manifest-helpers.py" telegram-chat-id \
915
- "$MANIFEST" "$OMEGA_HOME" 2>/dev/null | sed 's/^/ /' || true
916
- # When chat_id came from the prompt (not the manifest), persist directly.
917
933
  python3 - "$OMEGA_HOME" "$chat_id" <<'PY' || true
918
934
  import sys
919
935
  from pathlib import Path
@@ -922,12 +938,59 @@ sys.path.insert(0, str(home / "Agentik_Engine"))
922
938
  try:
923
939
  from omega_engine.vault import vault_write
924
940
  vault_write(home, "TELEGRAM_GROUP_ID", sys.argv[2])
925
- print(f"Telegram group_chat_id stored: {sys.argv[2]}")
941
+ print(f"AISB group chat_id stored: {sys.argv[2]}")
926
942
  except Exception as exc:
927
943
  print(f"(vault write skipped: {exc})")
928
944
  PY
929
945
  else
930
- info "no Telegram group_chat_id captured — set later with \`omega telegram set-group <id>\`"
946
+ info "no AISB group_chat_id captured — set later with \`omega telegram set-group <id>\`"
947
+ fi
948
+
949
+ # 3. Hermès bot — OPTIONAL. Skip cleanly if the operator just hits Enter.
950
+ echo
951
+ info "Hermès bot — OPTIONAL. Meta-companion on Anthropic API (you pay per call)."
952
+ info " Skip both prompts if you don't want Hermès yet — OmegaOS works fully without it."
953
+ ask hermes_token "Hermès Telegram bot token (blank = skip Hermès)" "${TELEGRAM_TOKEN_HERMES:-}"
954
+ if [ -n "$hermes_token" ]; then
955
+ if have curl; then
956
+ if ! curl -s "https://api.telegram.org/bot${hermes_token}/getMe" | grep -q '"ok":true'; then
957
+ err "Hermès Telegram token rejected by the API — continuing without Hermès"
958
+ hermes_token=""
959
+ fi
960
+ fi
961
+ fi
962
+ if [ -n "$hermes_token" ]; then
963
+ ask anthropic_key "Anthropic API key for Hermès (sk-ant-...; blank = set later)" \
964
+ "${ANTHROPIC_API_KEY_HERMES:-${ANTHROPIC_API_KEY:-}}"
965
+ {
966
+ echo "# Hermès bot (Layer 2 meta-companion, Anthropic API)"
967
+ printf 'TELEGRAM_TOKEN_HERMES=%s\n' "$hermes_token"
968
+ } >> "$sec/telegram.env"
969
+ if [ -n "$anthropic_key" ]; then
970
+ printf 'ANTHROPIC_API_KEY_HERMES=%s\n' "$anthropic_key" >> "$sec/telegram.env"
971
+ fi
972
+ chmod 600 "$sec/telegram.env"
973
+ python3 - "$OMEGA_HOME" "$hermes_token" "$anthropic_key" <<'PY' || true
974
+ import sys
975
+ from pathlib import Path
976
+ home = Path(sys.argv[1])
977
+ sys.path.insert(0, str(home / "Agentik_Engine"))
978
+ try:
979
+ from omega_engine.vault import vault_write
980
+ vault_write(home, "TELEGRAM_TOKEN_HERMES", sys.argv[2])
981
+ if sys.argv[3]:
982
+ vault_write(home, "ANTHROPIC_API_KEY_HERMES", sys.argv[3])
983
+ except Exception as exc:
984
+ print(f"(vault write skipped: {exc})")
985
+ PY
986
+ if [ -n "$anthropic_key" ]; then
987
+ ok "Hermès Telegram bot + Anthropic key wired"
988
+ else
989
+ info "Hermès Telegram bot wired (no Anthropic key yet — set with"
990
+ info " omega vault write ANTHROPIC_API_KEY_HERMES <sk-ant-...>)"
991
+ fi
992
+ else
993
+ info "Hermès skipped — wire later with \`omega telegram setup-hermes\`"
931
994
  fi
932
995
  return 0
933
996
  }
@@ -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.13"
191
+ __version__ = "0.19.15"
192
192
 
193
193
  __all__ = [
194
194
  "__version__",
@@ -2741,8 +2741,70 @@ def cmd_aisb(args: argparse.Namespace) -> int:
2741
2741
  return 2
2742
2742
 
2743
2743
 
2744
+ def _attach_or_spawn_chat(session_name: str, *,
2745
+ spawn_fn,
2746
+ label: str) -> int:
2747
+ """Attach to ``session_name`` tmux session, spawning it first if absent.
2748
+
2749
+ - Outside tmux : `os.execvp("tmux", ["tmux", "attach", "-t", session])`
2750
+ - Inside tmux : prints the switch command (we can't switch from a
2751
+ subprocess; the user needs to do it in their shell).
2752
+ Returns the exit code of execvp or 0 when we printed instructions.
2753
+
2754
+ This is the v0.19.15 UX: bare `omega` puts you in the master chat,
2755
+ bare `omega hermes` puts you in the Hermès chat. No whiptail menu
2756
+ by default — that's `omega menu` now.
2757
+ """
2758
+ import os
2759
+ from omega_engine import tmux
2760
+ home = _omega_home()
2761
+ if not tmux.is_alive(session_name):
2762
+ try:
2763
+ spawn_fn(home)
2764
+ except Exception as exc: # noqa: BLE001
2765
+ print(f" could not spawn {label}: {exc}")
2766
+ print(f" try manually: tmux new -d -s {session_name}")
2767
+ return 2
2768
+ # Inside tmux already? Tell the user to switch (we can't from here).
2769
+ if os.environ.get("TMUX"):
2770
+ print(f" already inside tmux — switch with:")
2771
+ print(f" tmux switch-client -t {session_name}")
2772
+ print(f" or detach (Ctrl-b d) then `omega` again.")
2773
+ return 0
2774
+ # Outside tmux — replace this process with `tmux attach`.
2775
+ print(f" attaching to {label} ({session_name})…")
2776
+ print(f" detach: Ctrl-b d (session keeps running)")
2777
+ try:
2778
+ os.execvp("tmux", ["tmux", "attach", "-t", session_name])
2779
+ except FileNotFoundError:
2780
+ print(" tmux not on PATH — install with `brew install tmux` "
2781
+ "or `apt-get install tmux`")
2782
+ return 2
2783
+ return 0 # unreachable (execvp replaced the process)
2784
+
2785
+
2744
2786
  def cmd_menu(_args: argparse.Namespace) -> int:
2745
- """`omega` with no args interactive whiptail menu (the on-ramp)."""
2787
+ """`omega` (no args) open the AISB master chat session in tmux.
2788
+
2789
+ v0.19.15 changed the default: typing `omega` used to open the whiptail
2790
+ menu, which Gareth correctly called bad UX ("type `omega`, talk to my
2791
+ master agent"). Now bare `omega` attaches to the always-on `AISB-chat`
2792
+ tmux session, spawning it on demand. The whiptail menu lives at
2793
+ `omega menu`.
2794
+ """
2795
+ from omega_engine import tmux
2796
+ return _attach_or_spawn_chat(
2797
+ "AISB-chat",
2798
+ spawn_fn=tmux.spawn_aisb_chat,
2799
+ label="AISB master chat",
2800
+ )
2801
+
2802
+
2803
+ def cmd_menu_whiptail(_args: argparse.Namespace) -> int:
2804
+ """`omega menu` — the explicit whiptail picker.
2805
+
2806
+ The old bare-omega behaviour, moved behind an explicit subcommand
2807
+ because the default is now the AISB chat (v0.19.15)."""
2746
2808
  from omega_engine.menu import run_menu
2747
2809
  return run_menu()
2748
2810
 
@@ -2825,6 +2887,53 @@ def cmd_hermes(args: argparse.Namespace) -> int:
2825
2887
  import json as _json
2826
2888
  sub = getattr(args, "hermes_cmd", None)
2827
2889
 
2890
+ # v0.19.15 — `omega hermes` with no subcommand opens the Hermès chat
2891
+ # tmux session. Same UX as bare `omega` → AISB chat, but for L2.
2892
+ if sub is None or sub == "chat":
2893
+ from omega_engine import tmux as _tx
2894
+ return _attach_or_spawn_chat(
2895
+ "Hermes-chat",
2896
+ spawn_fn=_tx.spawn_hermes_chat,
2897
+ label="Hermès meta-companion chat",
2898
+ )
2899
+ if sub == "chat-loop":
2900
+ # REPL that runs inside the Hermes-chat tmux session.
2901
+ # Uses Anthropic API directly (separate from AISB's Max OAuth).
2902
+ from omega_engine.aisb_chat import chat_once
2903
+ from omega_engine.vault import vault_read
2904
+ import os, sys
2905
+ home = _omega_home()
2906
+ api_key = (vault_read(home, "ANTHROPIC_API_KEY_HERMES") or "").strip()
2907
+ if not api_key:
2908
+ api_key = (os.environ.get("ANTHROPIC_API_KEY") or "").strip()
2909
+ if not api_key:
2910
+ print(" Hermès has no Anthropic API key — set one with:")
2911
+ print(" omega vault write ANTHROPIC_API_KEY_HERMES sk-ant-...")
2912
+ print(" (Hermès uses its OWN paid Anthropic budget, separate")
2913
+ print(" from OmegaOS's Claude Max OAuth — see LAYERS.md)")
2914
+ return 2
2915
+ os.environ["ANTHROPIC_API_KEY"] = api_key
2916
+ print(f" Hermès chat (Anthropic API) — /quit to exit")
2917
+ # We re-use chat_once but namespace history to a Hermès topic so
2918
+ # AISB's chat history doesn't bleed into Hermès's.
2919
+ HERMES_TOPIC = -999999
2920
+ while True:
2921
+ try:
2922
+ line = input("\nyou > ").strip()
2923
+ except (EOFError, KeyboardInterrupt):
2924
+ print("\n(exit)")
2925
+ return 0
2926
+ if not line:
2927
+ continue
2928
+ if line in ("/quit", "/exit"):
2929
+ return 0
2930
+ try:
2931
+ reply = chat_once(home, line, topic_id=HERMES_TOPIC)
2932
+ except Exception as exc: # noqa: BLE001
2933
+ print(f"hermes (error): {exc}")
2934
+ continue
2935
+ print(f"\nhermes > {reply}")
2936
+
2828
2937
  if sub == "status":
2829
2938
  st = H.status()
2830
2939
  if getattr(args, "json", False):
@@ -3774,10 +3883,14 @@ def cmd_cleanup(args: argparse.Namespace) -> int:
3774
3883
 
3775
3884
  def _build_parser() -> argparse.ArgumentParser:
3776
3885
  parser = argparse.ArgumentParser(prog="omega", description="Omega OS control CLI")
3777
- # When no subcommand is given, drop into the interactive menu instead of
3778
- # erroring. The cmd_menu function reads the whiptail menus and dispatches.
3886
+ # v0.19.15 bare `omega` now drops into the AISB master chat tmux
3887
+ # session (attach or spawn). `omega menu` keeps the legacy whiptail
3888
+ # picker for power users.
3779
3889
  parser.set_defaults(fn=cmd_menu)
3780
3890
  sub = parser.add_subparsers(dest="cmd")
3891
+ sub.add_parser("menu",
3892
+ help="open the interactive whiptail menu (the old `omega` behaviour)"
3893
+ ).set_defaults(fn=cmd_menu_whiptail)
3781
3894
  sub.add_parser("version", help="print the engine version").set_defaults(fn=cmd_version)
3782
3895
  p_doc = sub.add_parser("doctor", help="validate the deployment")
3783
3896
  p_doc.add_argument("--json", action="store_true",
@@ -4018,6 +4131,14 @@ def _build_parser() -> argparse.ArgumentParser:
4018
4131
  help="(observe) project slug")
4019
4132
  p_hebr.add_argument("task_id", nargs="?", default=None,
4020
4133
  help="(observe) task id whose .done.json to ingest")
4134
+ # v0.19.15 — `omega hermes chat` and `omega hermes chat-loop`: the
4135
+ # Hermès counterpart to `omega aisb chat`. `chat` spawns/attaches
4136
+ # the persistent Hermes-chat tmux session; `chat-loop` is the REPL
4137
+ # itself that runs inside that session (Anthropic API).
4138
+ he_sub.add_parser("chat",
4139
+ help="spawn (or attach to) the Hermes-chat tmux session")
4140
+ he_sub.add_parser("chat-loop",
4141
+ help="the Hermès REPL itself (called by the tmux session)")
4021
4142
 
4022
4143
  # `omega hermes-desktop` — Hermes Desktop remote backend
4023
4144
  p_hd = sub.add_parser(
@@ -248,19 +248,45 @@ def build_env(
248
248
  *,
249
249
  extra: dict[str, str] | None = None,
250
250
  creds_path: str | Path | None = None,
251
+ omega_home: str | Path | None = None,
251
252
  ) -> dict[str, str]:
252
253
  """Build the env dict for every Hermes invocation.
253
254
 
254
- Injects ``CLAUDE_CODE_OAUTH_TOKEN`` from the live credentials file +
255
- a few sane defaults (``HERMES_PROVIDER=anthropic``,
256
- ``HERMES_MODEL=claude-opus-4-7``). Caller env wins via ``extra``.
255
+ v0.19.14 Hermès now defaults to its OWN Anthropic API key (budget
256
+ isolation from the OmegaOS Claude Max OAuth):
257
+
258
+ 1. ``ANTHROPIC_API_KEY_HERMES`` from the encrypted vault → primary.
259
+ 2. ``ANTHROPIC_API_KEY`` already in the shell env → fallback.
260
+ 3. ``CLAUDE_CODE_OAUTH_TOKEN`` from ``~/.claude/.credentials.json``
261
+ → LAST RESORT only, with a clear warning that this routes Hermès
262
+ calls through your Max sub.
263
+
264
+ Caller env wins via ``extra``.
257
265
  """
258
- token = claude_oauth_token(creds_path)
259
266
  env = {**os.environ}
260
- env["CLAUDE_CODE_OAUTH_TOKEN"] = token
261
- # If the operator had stale anthropic env vars set, the OAuth token
262
- # takes precedence. We do NOT clear them — operators may have legit
263
- # API key setups for other tools.
267
+ # 1. Vault first.
268
+ key = ""
269
+ try:
270
+ from omega_engine.vault import vault_read
271
+ from pathlib import Path as _P
272
+ home = _P(omega_home) if omega_home else _P(
273
+ os.environ.get("OMEGA_HOME", str(_P.home() / "Omega"))
274
+ )
275
+ key = (vault_read(home, "ANTHROPIC_API_KEY_HERMES") or "").strip()
276
+ except Exception: # noqa: BLE001
277
+ key = ""
278
+ # 2. Shell env fallback.
279
+ if not key:
280
+ key = (os.environ.get("ANTHROPIC_API_KEY") or "").strip()
281
+ # 3. Last-resort OAuth (preserves the old behaviour without making
282
+ # Hermès depend on it).
283
+ if not key:
284
+ try:
285
+ env["CLAUDE_CODE_OAUTH_TOKEN"] = claude_oauth_token(creds_path)
286
+ except Exception: # noqa: BLE001
287
+ pass
288
+ if key:
289
+ env["ANTHROPIC_API_KEY"] = key
264
290
  env.setdefault("HERMES_PROVIDER", "anthropic")
265
291
  env.setdefault("HERMES_MODEL", "claude-opus-4-7")
266
292
  if extra:
@@ -232,11 +232,11 @@ def spawn_worker(project: str, task: str, *,
232
232
 
233
233
 
234
234
  def spawn_aisb_chat(omega_home: str | Path | None = None) -> str:
235
- """Spawn an AISB chat session — the Telegram-conversation fallback.
235
+ """Spawn the AISB master chat tmux session.
236
236
 
237
- Runs ``omega aisb chat-loop`` inside the session so the user can
238
- attach and have a conversation with the AISB master agent without
239
- needing Telegram connectivity.
237
+ Runs ``omega aisb chat-loop`` inside REPL conversation with the
238
+ AISB master agent on Claude Max OAuth. This is what bare `omega`
239
+ attaches to (v0.19.15+).
240
240
  """
241
241
  name = "AISB-chat"
242
242
  home = Path(omega_home or os.environ.get("OMEGA_HOME")
@@ -246,6 +246,21 @@ def spawn_aisb_chat(omega_home: str | Path | None = None) -> str:
246
246
  return name
247
247
 
248
248
 
249
+ def spawn_hermes_chat(omega_home: str | Path | None = None) -> str:
250
+ """Spawn the Hermès meta-companion chat tmux session.
251
+
252
+ Runs ``omega hermes chat-loop`` — REPL conversation with Hermès on
253
+ Anthropic API (separate budget from AISB's Max OAuth). This is what
254
+ `omega hermes` attaches to (v0.19.15+).
255
+ """
256
+ name = "Hermes-chat"
257
+ home = Path(omega_home or os.environ.get("OMEGA_HOME")
258
+ or Path.home() / "Omega")
259
+ omega_bin = home / "Agentik_Tools" / "bin" / "omega"
260
+ spawn(name, command=f"{omega_bin} hermes chat-loop")
261
+ return name
262
+
263
+
249
264
  # ---------------------------------------------------------------------------
250
265
  # Categorised view + health
251
266
  # ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "omega-engine"
3
- version = "0.19.13"
3
+ version = "0.19.15"
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"
@@ -95,13 +95,43 @@ class TestClaudeOAuthRead(unittest.TestCase):
95
95
 
96
96
 
97
97
  class TestBuildEnv(unittest.TestCase):
98
- def test_env_includes_oauth_token_and_defaults(self):
98
+ """v0.19.14 — Hermès prefers ANTHROPIC_API_KEY_HERMES from the vault,
99
+ falls back to ANTHROPIC_API_KEY env, then last-resort to the Max
100
+ OAuth at ``~/.claude/.credentials.json``."""
101
+
102
+ def test_env_uses_anthropic_key_env_when_set(self):
103
+ with tempfile.TemporaryDirectory() as tmp:
104
+ p = _fake_creds(Path(tmp) / "c.json", token="tk-Z")
105
+ old = os.environ.get("ANTHROPIC_API_KEY")
106
+ os.environ["ANTHROPIC_API_KEY"] = "sk-ant-test"
107
+ os.environ.pop("OMEGA_HOME", None)
108
+ try:
109
+ env = H.build_env(creds_path=p)
110
+ self.assertEqual(env.get("ANTHROPIC_API_KEY"), "sk-ant-test",
111
+ "Hermès must prefer ANTHROPIC_API_KEY over OAuth")
112
+ # OAuth NOT exposed when an Anthropic key is available.
113
+ self.assertNotIn("CLAUDE_CODE_OAUTH_TOKEN", env)
114
+ finally:
115
+ if old is None:
116
+ os.environ.pop("ANTHROPIC_API_KEY", None)
117
+ else:
118
+ os.environ["ANTHROPIC_API_KEY"] = old
119
+
120
+ def test_env_falls_back_to_oauth_when_no_anthropic_key(self):
99
121
  with tempfile.TemporaryDirectory() as tmp:
100
122
  p = _fake_creds(Path(tmp) / "c.json", token="tk-Z")
101
- env = H.build_env(creds_path=p)
102
- self.assertEqual(env["CLAUDE_CODE_OAUTH_TOKEN"], "tk-Z")
103
- self.assertEqual(env["HERMES_PROVIDER"], "anthropic")
104
- self.assertEqual(env["HERMES_MODEL"], "claude-opus-4-7")
123
+ old = os.environ.get("ANTHROPIC_API_KEY")
124
+ os.environ.pop("ANTHROPIC_API_KEY", None)
125
+ os.environ["OMEGA_HOME"] = tmp # no vault key here
126
+ try:
127
+ env = H.build_env(creds_path=p)
128
+ self.assertEqual(env.get("CLAUDE_CODE_OAUTH_TOKEN"), "tk-Z")
129
+ self.assertEqual(env["HERMES_PROVIDER"], "anthropic")
130
+ self.assertEqual(env["HERMES_MODEL"], "claude-opus-4-7")
131
+ finally:
132
+ os.environ.pop("OMEGA_HOME", None)
133
+ if old is not None:
134
+ os.environ["ANTHROPIC_API_KEY"] = old
105
135
 
106
136
  def test_env_extra_overrides_default(self):
107
137
  with tempfile.TemporaryDirectory() as tmp:
@@ -110,9 +140,27 @@ class TestBuildEnv(unittest.TestCase):
110
140
  extra={"HERMES_MODEL": "claude-sonnet-4-7"})
111
141
  self.assertEqual(env["HERMES_MODEL"], "claude-sonnet-4-7")
112
142
 
113
- def test_env_missing_token_propagates_error(self):
114
- with self.assertRaises(H.HermesError):
115
- H.build_env(creds_path=Path("/no/such/file"))
143
+ def test_env_no_creds_no_anthropic_returns_env_without_token(self):
144
+ """When neither an Anthropic key nor Claude OAuth is available,
145
+ build_env returns a usable env WITHOUT either token. The caller
146
+ (or Hermès itself) decides whether to error or skip; build_env
147
+ no longer raises — that was the v0.18 strict behaviour."""
148
+ old_anth = os.environ.get("ANTHROPIC_API_KEY")
149
+ old_home = os.environ.get("OMEGA_HOME")
150
+ os.environ.pop("ANTHROPIC_API_KEY", None)
151
+ with tempfile.TemporaryDirectory() as tmp:
152
+ os.environ["OMEGA_HOME"] = tmp
153
+ try:
154
+ env = H.build_env(creds_path=Path("/no/such/file"))
155
+ self.assertNotIn("ANTHROPIC_API_KEY", env)
156
+ self.assertNotIn("CLAUDE_CODE_OAUTH_TOKEN", env)
157
+ self.assertEqual(env["HERMES_PROVIDER"], "anthropic")
158
+ finally:
159
+ os.environ.pop("OMEGA_HOME", None)
160
+ if old_home is not None:
161
+ os.environ["OMEGA_HOME"] = old_home
162
+ if old_anth is not None:
163
+ os.environ["ANTHROPIC_API_KEY"] = old_anth
116
164
 
117
165
 
118
166
  # ---------------------------------------------------------------------------
@@ -140,7 +140,12 @@ class TestPostInstallCard(unittest.TestCase):
140
140
  self.assertIn("install complete", out)
141
141
  self.assertIn("status", out)
142
142
  self.assertIn("next steps", out)
143
- self.assertIn("omega status", out)
143
+ # v0.19.15 — bare `omega` is the AISB master chat, `omega hermes`
144
+ # is the L2 companion. Old card pointed at `omega status` as the
145
+ # entry; new card points at `omega` itself.
146
+ self.assertIn("omega", out)
147
+ self.assertIn("AISB", out)
148
+ self.assertIn("Hermès", out)
144
149
  self.assertIn("omega run", out)
145
150
  # No literal $C_DIM / $C_RST tokens
146
151
  self.assertNotIn("${C_DIM}", out)
@@ -475,20 +475,25 @@ class TestBrandColourIsOrange(unittest.TestCase):
475
475
  f"banner should use C_ORANGE on each of 8 rows (got {n})")
476
476
 
477
477
 
478
- class TestPostInstallCardMentionsAISBChat(unittest.TestCase):
479
- """The 'AISB-chat' tmux session is the conversational entry point when
480
- Telegram isn't wired. The post-install card MUST tell the operator how
481
- to attach to it; otherwise they finish the install with no UI."""
482
-
483
- def test_card_shows_attach_command(self):
478
+ class TestPostInstallCardSurfacesEntryPoints(unittest.TestCase):
479
+ """v0.19.15 bare `omega` is now the AISB chat attach (no longer needs
480
+ the explicit `tmux attach -t AISB-chat` in the card). The card must
481
+ still surface BOTH master entry points: `omega` for AISB and
482
+ `omega hermes` for the L2 companion. Plus the "every session in tmux"
483
+ discipline."""
484
+
485
+ def test_card_surfaces_aisb_and_hermes_entries(self):
484
486
  if not COMMON_SH.is_file():
485
487
  self.skipTest("common.sh not in repo")
486
488
  text = COMMON_SH.read_text()
487
- self.assertIn("tmux attach -t AISB-chat", text,
488
- "post_install_card must include the AISB-chat attach line — "
489
- "otherwise the operator has no way to discover their always-on "
490
- "Hermès session")
491
- # Also surface the "every session in tmux" expectation.
489
+ # Bare omega = AISB master.
490
+ self.assertIn("AISB", text,
491
+ "post_install_card must mention AISB as the master agent entry")
492
+ # `omega hermes` = L2.
493
+ self.assertIn("omega hermes", text,
494
+ "post_install_card must mention `omega hermes` as the L2 "
495
+ "companion entry — without it the user can't discover it")
496
+ # The "every session in tmux" expectation.
492
497
  self.assertIn("tmux", text)
493
498
 
494
499
 
@@ -1 +1 @@
1
- 0.19.13
1
+ 0.19.15
@@ -1,90 +1,147 @@
1
1
  # OmegaOS Layered Architecture
2
2
 
3
- > Five layers. Each independently usable. All five share ONE credential:
4
- > the Claude Code Max OAuth token at `~/.claude/.credentials.json`.
3
+ > Five layers. **Two independent credential domains.** **Two Telegram bots
4
+ > with separate tokens.** Hermès sits above; AISB+Oracle+Workers stay below.
5
+ > Each layer is independently usable.
6
+
7
+ ## The corrected model (v0.19.14)
5
8
 
6
9
  ```
7
10
  ┌──────────────────────────────────────────────────────────────────────┐
8
- │ Layer 1 ─ Human
9
- Telegram / CLI / web — operator intent in natural language
10
- └────────────────────────────────────────┬─────────────────────────────┘
11
-
12
- ┌────────────────────────────────────────▼─────────────────────────────┐
13
- │ Layer 2 ─ Hermes (Nous Research, opt-in) │
14
- Autonomous self-improving agent. Cross-platform gateway, scheduled
15
- │ automations, skill creation from experience, dialectic user model. │
16
- │ Uses CLAUDE_CODE_OAUTH_TOKEN from ~/.claude/.credentials.json. │
17
- Bridge: `omega hermes {install,link,status,ask}`
18
- └────────────────────────────────────────┬─────────────────────────────┘
19
-
20
- ┌────────────────────────────────────────▼─────────────────────────────┐
21
- Layer 3 AISB (Agentik Intake Service Bus)
22
- Telegram-first intake. Classifies the message, picks a topology,
23
- dispatches a mission to the Engine. Lives in $OMEGA_HOME.
24
- └────────────────────────────────────────┬─────────────────────────────┘
25
-
26
- ┌────────────────────────────────────────▼─────────────────────────────┐
27
- Layer 4 Oracle (the planner)
28
- Reads the mission, plans 1..N worker subtasks, each with a
29
- │ verify_cmd + file ownership. Persistent tmux session. │
30
- └────────────────────────────────────────┬─────────────────────────────┘
31
-
32
- ┌────────────────────────────────────────▼─────────────────────────────┐
33
- Layer 5Workers (executors)
34
- One per subtask. Persistent tmux session. Runs `claude -p` with
35
- the worker envelope + injected skill palette. Writes .done.json
36
- on completion. Verified by the audit gate before the parent's
37
- │ barrier resolves. │
38
- └──────────────────────────────────────────────────────────────────────┘
11
+ │ Layer 1 ─ Human (you)
12
+ Three doors:
13
+ │ 1. Telegram → Hermès bot (meta-companion, Anthropic API) │
14
+ 2. Telegram → AISB bot (OmegaOS native, Claude Max OAuth) │
15
+ │ 3. CLI / tmux (omega …, AISB-chat session) │
16
+ └────────┬───────────────────────────────────┬─────────────────────────┘
17
+
18
+ ▼ ▼
19
+ ┌──────────────────────┐ ┌────────────────────────────────────┐
20
+ Layer 2 ─ HERMÈS │ Layer 3 ─ AISB │
21
+ │ Meta-companion, │ ───────►│ OmegaOS intake / orchestrator │
22
+ scheduler, learner. │ dispatch│ Classifies, picks a topology, │
23
+ │ │ missions│ runs the mission. │
24
+ Auth: ANTHROPIC_API_ │ │
25
+ KEY_HERMES │ │ Auth: Claude Max OAuth
26
+ Bot: TELEGRAM_TOKEN_│ │ (~/.claude/.credentials.json)
27
+ │ HERMES │ │ Bot: TELEGRAM_TOKEN_AISB │
28
+ │ │ │
29
+ │ OPT-IN (skip if you │ │ Required for missions. │
30
+ don't want a paid │ │ Telegram bot is optional too —
31
+ Anthropic key) │ │ tmux AISB-chat is the fallback.
32
+ └──────────────────────┘ └─────────────┬──────────────────────┘
33
+
34
+
35
+ ┌────────────────────────────────────┐
36
+ Layer 4Oracle (planner)
37
+ │ Persistent tmux. Reads mission,
38
+ plans N workers with verify_cmd.
39
+ Auth: Claude Max OAuth
40
+ └─────────────┬──────────────────────┘
41
+
42
+
43
+ ┌────────────────────────────────────┐
44
+ │ Layer 5 ─ Workers (executors) │
45
+ │ One per subtask. Persistent tmux. │
46
+ │ Writes .done.json. Audit-gated. │
47
+ │ Auth: Claude Max OAuth │
48
+ └────────────────────────────────────┘
49
+ ```
50
+
51
+ ## Why TWO bots, TWO auth domains
52
+
53
+ | Concern | Hermès (L2) | AISB+Oracle+Workers (L3-L5) |
54
+ |---|---|---|
55
+ | **Telegram token** | `TELEGRAM_TOKEN_HERMES` | `TELEGRAM_TOKEN_AISB` |
56
+ | **LLM credential** | `ANTHROPIC_API_KEY_HERMES` (you pay per call) | Claude Max OAuth from `~/.claude/.credentials.json` (covered by your Max sub) |
57
+ | **What it does** | Meta-reasoning: schedules, suggestions, learning, dispatches missions DOWN to AISB | Executes verified-completion missions inside projects |
58
+ | **Budget risk** | You pay → bounded by your Anthropic spend limit | Claude Max — never charged per call |
59
+ | **Failure mode if absent** | Skip Hermès → OmegaOS still works (CLI + AISB bot) | If absent, no missions → not optional |
60
+
61
+ This split is intentional:
62
+
63
+ 1. **Budget isolation.** Hermès can run a tight schedule loop, learn from
64
+ observations, propose missions — without ever touching your Max quota.
65
+ AISB+Workers can crank through 50-step missions on Max OAuth — without
66
+ ever burning your Anthropic API budget.
67
+ 2. **Independent identity.** Hermès has its own Telegram username, its
68
+ own bot personality, its own conversation history. AISB stays
69
+ project-focused (one bot per OmegaOS install, topics per project).
70
+ 3. **Authority gradient.** Hermès is *above* OmegaOS — it can issue
71
+ commands to AISB but doesn't run inside it. AISB is *the* runtime
72
+ for OmegaOS missions.
73
+
74
+ ## Per-project Telegram structure
75
+
76
+ OmegaOS treats Telegram as the operator's command surface:
77
+
78
+ ```
79
+ AISB Group (Telegram supergroup with topics enabled)
80
+
81
+ ├── Topic "general" — chat with AISB, status questions
82
+ ├── Topic "project: dentistry" — dispatch missions for the Dentistry project
83
+ ├── Topic "project: kommu" — dispatch missions for the Kommu project
84
+ ├── Topic "project: causio" — dispatch missions for the Causio project
85
+ └── … (one topic per project, mapped in
86
+ Agentik_SSOT/projects/<slug>/registry.yaml)
39
87
  ```
40
88
 
41
- ## Why this shape
89
+ A message posted in a project topic = a mission for THAT project. AISB
90
+ reads `message_thread_id` (the topic_id), looks up the slug, runs the
91
+ mission in the project's context (cwd = project root, env =
92
+ project-scoped secrets).
93
+
94
+ Hermès operates ABOVE the group. It can:
95
+
96
+ * Receive a DM in **its own** Telegram bot from you.
97
+ * Decide: "this is a mission for project X" → send a message INTO
98
+ the AISB group's topic-X (Hermès posts as a member of the group).
99
+ * Or: "this is a meta-question" → answer directly via Anthropic API.
100
+ * Or: "this is an autonomous trigger" (cron, observation) → dispatch
101
+ a fresh mission into a project topic.
42
102
 
43
- | Layer | Role | Runtime | Credential |
44
- |---|---|---|---|
45
- | 1 | Intent | Human | n/a |
46
- | 2 | Autonomous companion | Hermes (Python, ~/.hermes) | Claude Max OAuth |
47
- | 3 | Mission intake | OmegaOS engine | Claude Max OAuth |
48
- | 4 | Planning | Persistent tmux + `claude -p` | Claude Max OAuth |
49
- | 5 | Execution | Persistent tmux + `claude -p` | Claude Max OAuth |
103
+ ## Install / secrets layout (corrected)
50
104
 
51
- Every layer below #1 calls Claude through the same OAuth credential. No
52
- API key burn, no separate auth surface, no extra account to manage. The
53
- token rotates server-side via the standard Claude Code refresh flow;
54
- every layer reads the live file (`~/.claude/.credentials.json`) on each
55
- call so refresh propagates automatically.
105
+ | Vault key | Holds | Who reads it |
106
+ |---|---|---|
107
+ | `TELEGRAM_TOKEN_AISB` | AISB bot token (from BotFather, bot A) | `omega telegram bridge` + AISB daemon |
108
+ | `TELEGRAM_TOKEN_HERMES` | Hermès bot token (from BotFather, bot B) | Hermès daemon only |
109
+ | `ANTHROPIC_API_KEY_HERMES` | Hermès's own paid Anthropic key | Hermès daemon only |
110
+ | `TELEGRAM_GROUP_ID` | AISB supergroup chat_id | AISB daemon (to scope missions) |
111
+ | `TELEGRAM_TOKEN` | **back-compat alias** for `TELEGRAM_TOKEN_AISB` | Old install paths |
112
+ | Claude Max OAuth | The OAuth at `~/.claude/.credentials.json` | AISB, Oracle, all Workers |
113
+
114
+ Claude Max OAuth is **never duplicated in the vault** — it lives in
115
+ `~/.claude/.credentials.json` and every layer below Hermès reads it live
116
+ on every call so refresh rotations propagate naturally.
56
117
 
57
118
  ## Layer choice cheat-sheet
58
119
 
59
120
  | You want to | Use |
60
121
  |---|---|
61
- | One-shot CLI question | Layer 5 directly: `claude -p "..."` |
62
- | Verified-completion multi-worker mission | Layer 3-5: `omega run "..."` |
63
- | Recurring scheduled mission (cron) | Layer 3-5: `omega cadence schedule ...` |
64
- | Cross-platform chat (Telegram, Discord, …) | Layer 2: `hermes gateway` |
65
- | Skill creation from experience over time | Layer 2: Hermes's learning loop |
66
- | Long-running autonomous research | Layer 2: Hermes + scheduled task |
67
- | Visual codebase exploration | Sidecar: `omega ua run <project>` |
68
-
69
- ## Bridges
70
-
71
- * `omega hermes` — Layer 2 ↔ rest of stack. Hermes can call OmegaOS via
72
- the `omega` CLI (any of layers 3-5). OmegaOS can call Hermes via
73
- `omega hermes ask "..."`.
74
- * `omega ua` — Understand-Anything sidecar. Builds an interactive
75
- knowledge graph of any project; `omega ua consume` lands the graph
76
- into the Engine's GraphRetriever so Oracle/workers can answer
77
- "where in the code is X?" via Graph RAG.
78
- * `omega ma` — Anthropic Managed Agents (opt-in, API-key-only). NOT
79
- part of the OAuth stack; only used when explicitly opted into per
80
- topology entry.
122
+ | Quick chat with the meta-companion | Hermès bot (DM) Anthropic API |
123
+ | Run a mission on a project | AISB project topic OR `omega run` from project dir |
124
+ | One-shot CLI question (no orchestration) | `claude -p "..."` directly |
125
+ | Verified-completion multi-worker mission | `omega run` / AISB topic engages Oracle + Workers |
126
+ | Recurring scheduled mission | `omega cadence schedule …` wakes through AISB |
127
+ | Cross-platform autonomous loop | Hermès (Anthropic API key, scheduled tasks) |
128
+ | Audit a project | `/codeaudit` etc. from project — Quality Arsenal |
81
129
 
82
130
  ## What this is NOT
83
131
 
84
- * Hermes is not a "wrapper" around OmegaOSit has its own loop, its
85
- own skills, its own memory. The bridge gives both runtimes the
86
- ability to delegate to each other.
87
- * Layer 2 is OPTIONAL. OmegaOS without Hermes is a complete agentic
88
- OS (layers 1+3-5). Adding Hermes buys you the autonomous companion
89
- + cross-platform gateway + learning loop. Removing Hermes removes
90
- none of OmegaOS's verified-completion guarantees.
132
+ * Hermès is NOT a wrapper around AISBthey're separate runtimes with
133
+ separate credentials and separate Telegram identities. The bridge gives
134
+ both the ability to delegate to each other.
135
+ * AISB does NOT need Hermès to function. Skip Hermès install → OmegaOS
136
+ is a complete agentic OS with verified completion. Add Hermès when you
137
+ want meta-reasoning + autonomous loops above your projects.
138
+ * The Anthropic API key Hermès uses is NOT shared with anything else.
139
+ AISB+Oracle+Workers stay on Max OAuth, always.
140
+
141
+ ## v0.19.x evolution
142
+
143
+ | Version | Change |
144
+ |---|---|
145
+ | v0.18 | Single OAuth model (Hermès on Claude Max) |
146
+ | **v0.19.14** | **Split: Hermès on `ANTHROPIC_API_KEY_HERMES`; AISB on Max OAuth; two Telegram bots** |
147
+ | (planned v0.19.15+) | Dedicated Hermès daemon (its own poll loop), Hermès→AISB dispatch verbs |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentikos/omega-os",
3
- "version": "0.19.13",
3
+ "version": "0.19.15",
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"