@agentikos/omega-os 0.19.37 → 0.19.39

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 (39) hide show
  1. package/bin/omega-os.js +6 -1
  2. package/bootstrap/lib/steps.sh +43 -0
  3. package/install.sh +5 -0
  4. package/omega/Agentik_Engine/omega_engine/__init__.py +1 -1
  5. package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
  7. package/omega/Agentik_Engine/omega_engine/__pycache__/paperclip_bridge.cpython-313.pyc +0 -0
  8. package/omega/Agentik_Engine/omega_engine/__pycache__/prompt_audit.cpython-313.pyc +0 -0
  9. package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
  10. package/omega/Agentik_Engine/omega_engine/__pycache__/tui.cpython-313.pyc +0 -0
  11. package/omega/Agentik_Engine/omega_engine/cli.py +73 -0
  12. package/omega/Agentik_Engine/omega_engine/paperclip_bridge.py +110 -0
  13. package/omega/Agentik_Engine/omega_engine/prompt_audit.py +395 -0
  14. package/omega/Agentik_Engine/omega_engine/tmux.py +16 -0
  15. package/omega/Agentik_Engine/omega_engine/tui.py +269 -67
  16. package/omega/Agentik_Engine/pyproject.toml +1 -1
  17. package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313-pytest-8.4.2.pyc +0 -0
  18. package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313.pyc +0 -0
  19. package/omega/Agentik_Engine/tests/__pycache__/test_paperclip_status.cpython-313-pytest-8.4.2.pyc +0 -0
  20. package/omega/Agentik_Engine/tests/__pycache__/test_paperclip_status.cpython-313.pyc +0 -0
  21. package/omega/Agentik_Engine/tests/__pycache__/test_prompt_audit.cpython-313-pytest-8.4.2.pyc +0 -0
  22. package/omega/Agentik_Engine/tests/__pycache__/test_prompt_audit.cpython-313.pyc +0 -0
  23. package/omega/Agentik_Engine/tests/__pycache__/test_tui_runtime.cpython-313-pytest-8.4.2.pyc +0 -0
  24. package/omega/Agentik_Engine/tests/__pycache__/test_tui_runtime.cpython-313.pyc +0 -0
  25. package/omega/Agentik_Engine/tests/test_installer_wiring.py +130 -0
  26. package/omega/Agentik_Engine/tests/test_paperclip_status.py +142 -0
  27. package/omega/Agentik_Engine/tests/test_prompt_audit.py +199 -0
  28. package/omega/Agentik_Engine/tests/test_tui_runtime.py +106 -0
  29. package/omega/Agentik_SSOT/VERSION +1 -1
  30. package/omega/Agentik_SSOT/docs/AUDIT-V0.19.38.md +90 -0
  31. package/omega/Agentik_SSOT/docs/AUDIT-V0.19.39.md +161 -0
  32. package/omega/Agentik_SSOT/rules/audit-gates.md +189 -0
  33. package/omega/Agentik_SSOT/rules/constitution.md +7 -0
  34. package/omega/Agentik_SSOT/rules/orchestration.md +215 -0
  35. package/omega/Agentik_SSOT/rules/prompt-protocols.md +219 -0
  36. package/omega/Agentik_SSOT/rules/scope-safety.md +197 -0
  37. package/omega/Agentik_SSOT/rules/three-laws.md +214 -0
  38. package/omega/Agentik_SSOT/rules/verified-completion.md +216 -0
  39. package/package.json +1 -1
package/bin/omega-os.js CHANGED
@@ -36,7 +36,12 @@ console.log(" Omega OS — npx bootstrap");
36
36
  console.log(" -> handing off to install.sh");
37
37
  console.log("");
38
38
 
39
- const args = process.argv.slice(2);
39
+ // Drop the literal `--` separator if npx/npm forwarded it. Without this,
40
+ // `npx -y @agentikos/omega-os@latest -- --full` lands in install.sh as
41
+ // `install.sh -- --full`, and the case-block rejects the unknown `--`.
42
+ const rawArgs = process.argv.slice(2);
43
+ const args = rawArgs.filter((a) => a !== "--");
44
+
40
45
  const result = spawnSync("bash", [installer, ...args], {
41
46
  stdio: "inherit",
42
47
  cwd: pkgRoot,
@@ -461,6 +461,49 @@ PY
461
461
  return $?
462
462
  }
463
463
 
464
+ # --- 38 -----------------------------------------------------------------------
465
+ #
466
+ # step_personas — seed the canonical OmegaOS context + every LLM's
467
+ # expected filename for both chat sessions.
468
+ #
469
+ # Without this step, the canonical OMEGAOS-CONTEXT.md and the per-LLM
470
+ # persona files (CLAUDE.md / GEMINI.md / AGENTS.md / QWEN.md /
471
+ # .opencode/CONTEXT.md / .continue/CONTEXT.md / CONVENTIONS.md /
472
+ # HERMES.md) only land the FIRST time the user spawns AISB-chat or
473
+ # Hermès-chat. Eager seeding lets the operator inspect + edit the
474
+ # canonical at install time and guarantees `omega doctor` sees every
475
+ # persona file before the first session.
476
+ #
477
+ # Idempotent — re-runs only write files whose content actually differs
478
+ # from the canonical.
479
+ step_personas() {
480
+ PYTHONPATH="$OMEGA_HOME/Agentik_Engine" python3 - <<PY 2>>"$LOG_FILE"
481
+ import os
482
+ from pathlib import Path
483
+ os.environ["OMEGA_HOME"] = "$OMEGA_HOME"
484
+ home = Path("$OMEGA_HOME")
485
+ from omega_engine.personas import (
486
+ ensure_canonical, write_all_personas, canonical_path, supported_llm_ids,
487
+ )
488
+
489
+ src = ensure_canonical(home)
490
+ print(f" canonical: {src.relative_to(home)} ({src.stat().st_size} bytes)")
491
+
492
+ # Mirror to BOTH chat-context dirs so every LLM has its persona ready
493
+ # from day one — no first-spawn lazy materialization.
494
+ for label in ("aisb-master", "hermes"):
495
+ ctx = home / "Agentik_Coding" / "chat-contexts" / label
496
+ written = write_all_personas(home, ctx)
497
+ # Count EVERY persona file (root + subdir like .opencode/CONTEXT.md).
498
+ files = sorted(str(p.relative_to(ctx))
499
+ for p in ctx.rglob("*.md"))
500
+ print(f" {label}/: {len(files)} persona files ({', '.join(files)})")
501
+
502
+ print(f" supported LLMs: {len(supported_llm_ids())}")
503
+ PY
504
+ return $?
505
+ }
506
+
464
507
  # --- 40 -----------------------------------------------------------------------
465
508
  #
466
509
  # step_clis — install system CLIs + Printing Press CLI library.
package/install.sh CHANGED
@@ -62,6 +62,10 @@ while [ $# -gt 0 ]; do
62
62
  fi
63
63
  ;;
64
64
  -h|--help) awk 'NR==1 && /^#!/ {next} /^#/ {sub(/^#[ ]?/, ""); print; next} {exit}' "$0"; exit 0 ;;
65
+ # Accept the literal `--` separator as a no-op so recipes like
66
+ # `npx -y @agentikos/omega-os@latest -- --full` survive even if the
67
+ # JS launcher's filter is bypassed.
68
+ --) shift ;;
65
69
  *) die "unknown argument: $1" ;;
66
70
  esac
67
71
  done
@@ -182,6 +186,7 @@ STEPS=(
182
186
  "35-providers:step_providers"
183
187
  "36-tmux-config:step_tmux_config"
184
188
  "37-hermes-brief:step_hermes_brief"
189
+ "38-personas:step_personas"
185
190
  # v0.19.21 — MCP install dropped from the default sequence. MCPs are
186
191
  # token-expensive (each call pays the protocol round-trip + the
187
192
  # server's system prompt overhead). We replace with system CLIs +
@@ -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.37"
191
+ __version__ = "0.19.39"
192
192
 
193
193
  __all__ = [
194
194
  "__version__",
@@ -440,6 +440,79 @@ def cmd_doctor(args: argparse.Namespace) -> int:
440
440
  except Exception as exc: # noqa: BLE001
441
441
  line("FAIL", f"agent suite check failed: {exc}")
442
442
 
443
+ # 7a-bis. Multi-LLM personas — canonical + per-LLM mirrors for the
444
+ # two chat contexts (AISB-master + Hermès). Step 38-personas should
445
+ # have materialized these at install time; if they're missing the
446
+ # user can re-run `omega menu-tui` → "Re-seed personas".
447
+ section("personas")
448
+ try:
449
+ from omega_engine.personas import canonical_path, supported_llm_ids
450
+ canon = canonical_path(home)
451
+ if canon.exists():
452
+ line("ok", f"canonical: {canon.relative_to(home)} ({canon.stat().st_size}B)")
453
+ else:
454
+ line("warn",
455
+ f"canonical missing at {canon.relative_to(home)} — "
456
+ "spawn AISB-chat once or re-run install step 38-personas")
457
+ # Each chat context should have at least one persona file per LLM
458
+ # subgroup. We count both root *.md and subdir CONTEXT.md.
459
+ for label in ("aisb-master", "hermes"):
460
+ ctx = home / "Agentik_Coding" / "chat-contexts" / label
461
+ if not ctx.is_dir():
462
+ line("warn", f"chat-contexts/{label}/ not seeded yet")
463
+ continue
464
+ files = sorted(p.relative_to(ctx).as_posix()
465
+ for p in ctx.rglob("*.md") if p.is_file())
466
+ if files:
467
+ line("ok",
468
+ f"chat-contexts/{label}/: {len(files)} persona file(s) "
469
+ f"({', '.join(files[:4])}{'...' if len(files) > 4 else ''})")
470
+ else:
471
+ line("warn",
472
+ f"chat-contexts/{label}/ exists but has no persona files")
473
+ line("ok", f"supported LLM persona ids: {len(supported_llm_ids())}")
474
+ except Exception as exc: # noqa: BLE001
475
+ line("FAIL", f"personas check failed: {exc}")
476
+
477
+ # 7c. Prompt audit — each AISB role's prompt must reference the
478
+ # Three Laws + LMC protocol + done.json contract. Otherwise the
479
+ # agent runs blind and orchestration breaks at the first dispatch.
480
+ section("prompts")
481
+ try:
482
+ from omega_engine.prompt_audit import audit_aisb_suite
483
+ rep = audit_aisb_suite(home)
484
+ if not rep.per_agent:
485
+ line("warn", "no AISB prompts found — step 25-aisb-suite may have been skipped")
486
+ else:
487
+ for r in rep.per_agent:
488
+ status = "ok" if r.score >= 80 else ("warn" if r.score >= 60 else "FAIL")
489
+ line(status, f"{r.agent_id}: {r.score}/100"
490
+ + (f" — missing: {', '.join(r.violations[:2])}" if r.violations else ""))
491
+ avg_status = "ok" if rep.average_score >= 80 else "warn"
492
+ line(avg_status, f"average suite score: {rep.average_score:.1f}/100")
493
+ if rep.missing_critical:
494
+ line("warn", f"weak prompts (<60): {', '.join(rep.missing_critical)}")
495
+ except Exception as exc: # noqa: BLE001
496
+ line("FAIL", f"prompt audit failed: {exc}")
497
+
498
+ # 7d. Orchestration chain — AISB → Oracle → Worker → Checker must
499
+ # all be wired and share the LMC protocol vocabulary.
500
+ section("orchestration")
501
+ try:
502
+ from omega_engine.prompt_audit import orchestration_health
503
+ oh = orchestration_health(home)
504
+ for k, v in (("aisb_master_present", "AISB master prompt"),
505
+ ("oracle_present", "Oracle role prompt"),
506
+ ("workers_role_present", "Worker-class prompts"),
507
+ ("checker_present", "Checker prompts (Seraph/Smith)"),
508
+ ("lmc_protocol_present", "LMC protocol document")):
509
+ line("ok" if oh.get(k) else "warn", v)
510
+ overlap = oh.get("shared_vocab_overlap", 0.0)
511
+ line("ok" if overlap >= 0.6 else "warn",
512
+ f"shared `.done.json` vocab: {overlap*100:.0f}% of agents")
513
+ except Exception as exc: # noqa: BLE001
514
+ line("FAIL", f"orchestration check failed: {exc}")
515
+
443
516
  # 7b. Per-project vaults
444
517
  section("project vaults")
445
518
  try:
@@ -26,6 +26,7 @@ from __future__ import annotations
26
26
  import json
27
27
  import os
28
28
  import shutil
29
+ import socket
29
30
  import subprocess
30
31
  import time
31
32
  from dataclasses import asdict, dataclass, field
@@ -584,3 +585,112 @@ def start_server(bind: str = "loopback", *, detach: bool = True) -> int:
584
585
  )
585
586
  return proc.pid
586
587
  return subprocess.call(args)
588
+
589
+
590
+ # ---------------------------------------------------------------------------
591
+ # Live status probe — is the Paperclip dashboard actually running?
592
+ # ---------------------------------------------------------------------------
593
+
594
+
595
+ @dataclass
596
+ class PaperclipStatus:
597
+ """Snapshot of whether the Paperclip dashboard daemon is alive.
598
+
599
+ Consumed by the TUI to render ●/○ next to Paperclip menu items so the
600
+ operator can tell at a glance whether the dashboard is reachable.
601
+ """
602
+
603
+ running: bool
604
+ pid: int | None # process PID if alive (None for port-scan hits)
605
+ port: int | None # port the dashboard listens on (8080 default)
606
+ url: str | None # http://host:port if running, else None
607
+ detection: str # "pidfile" | "port-scan" | "none"
608
+
609
+
610
+ def is_running(omega_home: Path | str | None = None,
611
+ *,
612
+ port: int = 8080,
613
+ host: str = "127.0.0.1") -> PaperclipStatus:
614
+ """Probe the Paperclip daemon — running or not?
615
+
616
+ Detection strategy (cheap → expensive, returns on first hit):
617
+ 1. Read ``$PAPERCLIP_HOME/run/dashboard.pid`` (default ``~/.paperclip``).
618
+ If file exists AND ``os.kill(pid, 0)`` succeeds → running.
619
+ 2. Else try TCP connect to ``<host>:<port>`` with a 0.2s timeout.
620
+ If accept → running but no pidfile (caller may warn).
621
+ 3. Else → not running.
622
+
623
+ Never raises. Never blocks more than ~0.3s total.
624
+
625
+ Args:
626
+ omega_home: Accepted for API symmetry with the rest of the bridge;
627
+ the actual pidfile lives under ``$PAPERCLIP_HOME`` (resolved
628
+ via :func:`paperclip_home`), not under ``$OMEGA_HOME``.
629
+ port: TCP port to probe in the fallback path. Default 8080.
630
+ host: Loopback host to probe. Default 127.0.0.1.
631
+
632
+ Returns:
633
+ :class:`PaperclipStatus` — never None.
634
+ """
635
+ del omega_home # signature consistency only; pidfile lives under PAPERCLIP_HOME
636
+
637
+ # ---- 1. Pidfile check (cheapest) -----------------------------------
638
+ pidfile = paperclip_home() / "run" / "dashboard.pid"
639
+ if pidfile.exists():
640
+ try:
641
+ pid_text = pidfile.read_text().strip()
642
+ pid = int(pid_text)
643
+ except (OSError, ValueError):
644
+ pid = None
645
+ if pid is not None and pid > 0:
646
+ alive = False
647
+ try:
648
+ os.kill(pid, 0)
649
+ alive = True
650
+ except ProcessLookupError:
651
+ alive = False
652
+ except PermissionError:
653
+ # PID exists but is owned by another user — still proves
654
+ # SOMETHING is alive at that PID, so treat as running.
655
+ alive = True
656
+ except OSError:
657
+ alive = False
658
+ if alive:
659
+ return PaperclipStatus(
660
+ running=True,
661
+ pid=pid,
662
+ port=port,
663
+ url=f"http://localhost:{port}",
664
+ detection="pidfile",
665
+ )
666
+ # Stale pidfile — fall through to port scan.
667
+
668
+ # ---- 2. Port scan fallback (~0.2s) ---------------------------------
669
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
670
+ try:
671
+ sock.settimeout(0.2)
672
+ try:
673
+ sock.connect((host, port))
674
+ return PaperclipStatus(
675
+ running=True,
676
+ pid=None,
677
+ port=port,
678
+ url=f"http://localhost:{port}",
679
+ detection="port-scan",
680
+ )
681
+ except (OSError, socket.timeout):
682
+ pass
683
+ finally:
684
+ try:
685
+ sock.close()
686
+ except OSError:
687
+ pass
688
+
689
+ # ---- 3. Not running -------------------------------------------------
690
+ return PaperclipStatus(
691
+ running=False,
692
+ pid=None,
693
+ port=None,
694
+ url=None,
695
+ detection="none",
696
+ )