@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.
- package/bin/omega-os.js +6 -1
- package/bootstrap/lib/steps.sh +43 -0
- package/install.sh +5 -0
- 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__/paperclip_bridge.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/prompt_audit.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 +73 -0
- package/omega/Agentik_Engine/omega_engine/paperclip_bridge.py +110 -0
- package/omega/Agentik_Engine/omega_engine/prompt_audit.py +395 -0
- package/omega/Agentik_Engine/omega_engine/tmux.py +16 -0
- package/omega/Agentik_Engine/omega_engine/tui.py +269 -67
- package/omega/Agentik_Engine/pyproject.toml +1 -1
- package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_installer_wiring.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_paperclip_status.cpython-313-pytest-8.4.2.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_paperclip_status.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_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_installer_wiring.py +130 -0
- package/omega/Agentik_Engine/tests/test_paperclip_status.py +142 -0
- package/omega/Agentik_Engine/tests/test_prompt_audit.py +199 -0
- package/omega/Agentik_Engine/tests/test_tui_runtime.py +106 -0
- package/omega/Agentik_SSOT/VERSION +1 -1
- package/omega/Agentik_SSOT/docs/AUDIT-V0.19.38.md +90 -0
- package/omega/Agentik_SSOT/docs/AUDIT-V0.19.39.md +161 -0
- package/omega/Agentik_SSOT/rules/audit-gates.md +189 -0
- package/omega/Agentik_SSOT/rules/constitution.md +7 -0
- package/omega/Agentik_SSOT/rules/orchestration.md +215 -0
- package/omega/Agentik_SSOT/rules/prompt-protocols.md +219 -0
- package/omega/Agentik_SSOT/rules/scope-safety.md +197 -0
- package/omega/Agentik_SSOT/rules/three-laws.md +214 -0
- package/omega/Agentik_SSOT/rules/verified-completion.md +216 -0
- package/package.json +1 -1
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"""Prompt audit — verify the AISB agent prompts are well-formed AND
|
|
2
|
+
reference the contracts they MUST reference.
|
|
3
|
+
|
|
4
|
+
Without this, the engine ships agents that read like generic system prompts
|
|
5
|
+
and the Three Laws / LMC protocol / verified-completion (`.done.json`)
|
|
6
|
+
contracts silently drift out of the role files. Orchestration breaks at
|
|
7
|
+
the first dispatch because a worker doesn't know its done-signal contract
|
|
8
|
+
or an oracle doesn't know it must enforce the laws.
|
|
9
|
+
|
|
10
|
+
The audit applies per-file (each role's prompt is scored in isolation —
|
|
11
|
+
not after the engine concatenates LMC + shared protocols at spawn time)
|
|
12
|
+
because the file on disk is what an operator reads and edits. If a role's
|
|
13
|
+
isolated file is silent on the Three Laws, the operator can't tell the
|
|
14
|
+
contract is present, and any edit that breaks the loader will silently
|
|
15
|
+
strip the laws too.
|
|
16
|
+
|
|
17
|
+
Public API:
|
|
18
|
+
|
|
19
|
+
- ``audit_agent_prompt(path)`` -> AgentPromptReport (score 0..100, checks dict)
|
|
20
|
+
- ``audit_aisb_suite(omega_home)`` -> SuiteReport (per-agent + averages)
|
|
21
|
+
- ``orchestration_health(omega_home)`` -> dict of presence + overlap
|
|
22
|
+
|
|
23
|
+
Only stdlib (``pathlib``, ``re``, ``dataclasses``). No new deps.
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import re
|
|
28
|
+
from dataclasses import dataclass, field
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Optional
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Data classes — what each audit returns
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class CheckResult:
|
|
40
|
+
"""One contract check on one prompt file."""
|
|
41
|
+
|
|
42
|
+
passed: bool
|
|
43
|
+
evidence: Optional[str] = None # short matched snippet, None if not found
|
|
44
|
+
points: int = 0 # points awarded for this check
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class AgentPromptReport:
|
|
49
|
+
"""Audit result for a single agent prompt .md file."""
|
|
50
|
+
|
|
51
|
+
agent_id: str
|
|
52
|
+
file_path: str
|
|
53
|
+
score: int = 0 # 0..100
|
|
54
|
+
checks: dict[str, CheckResult] = field(default_factory=dict)
|
|
55
|
+
violations: list[str] = field(default_factory=list) # human-readable
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class SuiteReport:
|
|
60
|
+
"""Audit result for the whole AISB suite."""
|
|
61
|
+
|
|
62
|
+
per_agent: list[AgentPromptReport] = field(default_factory=list)
|
|
63
|
+
average_score: float = 0.0
|
|
64
|
+
missing_critical: list[str] = field(default_factory=list) # score < 60
|
|
65
|
+
orchestration_chain_intact: bool = False
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Contract patterns — what every well-formed agent prompt should reference
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Three Laws — case-insensitive search for any of these markers
|
|
74
|
+
_RE_THREE_LAWS = re.compile(
|
|
75
|
+
r"(three\s+laws|first\s+law|second\s+law|third\s+law|"
|
|
76
|
+
r"law\s+1|law\s+2|law\s+3)",
|
|
77
|
+
re.IGNORECASE,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# LMC protocol — concept or filename
|
|
81
|
+
_RE_LMC = re.compile(
|
|
82
|
+
r"(lmc[\s\-_]*(protocol|gate)?|lead[\s\-]+manager[\s\-]+checker|"
|
|
83
|
+
r"lmc-protocol\.md)",
|
|
84
|
+
re.IGNORECASE,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Verified-completion contract — `.done.json`
|
|
88
|
+
_RE_DONE_JSON = re.compile(r"\.done\.json|done\.json", re.IGNORECASE)
|
|
89
|
+
|
|
90
|
+
# Done-marker tooling — the script that writes the .done.json signal
|
|
91
|
+
_RE_DONE_MARKER = re.compile(
|
|
92
|
+
r"(worker[\s\-]?mark[\s\-]?done|oracle[\s\-]?mark[\s\-]?done|"
|
|
93
|
+
r"mark[\s\-]?done\.sh|write_done\b|done_signal|"
|
|
94
|
+
r"done[\s\-]?marker)",
|
|
95
|
+
re.IGNORECASE,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Scope / role-specific responsibilities — at least one of these tokens
|
|
99
|
+
# tells us the file declares what the agent owns / is allowed to touch.
|
|
100
|
+
_RE_SCOPE = re.compile(
|
|
101
|
+
r"(scope|files?_owned|owns?\s+R[\s\-]?\d+|responsibilit(y|ies)|"
|
|
102
|
+
r"^\s*role\b|owned\s+files|files\s+owned|what\s+you\s+own|"
|
|
103
|
+
r"\bowns\b)",
|
|
104
|
+
re.IGNORECASE | re.MULTILINE,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Fresh context handoff vocabulary
|
|
108
|
+
_RE_FRESH_CONTEXT = re.compile(
|
|
109
|
+
r"fresh[\s\-]+context|self[\s\-]+contained\s+brief|"
|
|
110
|
+
r"self[\s\-]+contained\s+context",
|
|
111
|
+
re.IGNORECASE,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Banned phrases — the no-time-panic global rule (rule 46). These are the
|
|
115
|
+
# action-oriented forms that authorize cheating, NOT the descriptive forms
|
|
116
|
+
# (e.g. "lightweight utility agent" describes a haiku tier — fine).
|
|
117
|
+
# We explicitly DO NOT include bare "lightweight" because templates
|
|
118
|
+
# legitimately describe haiku-tier agents as lightweight (architecture
|
|
119
|
+
# descriptor, not a shortcut authorization).
|
|
120
|
+
_BANNED_PHRASES = [
|
|
121
|
+
"streamlined approach",
|
|
122
|
+
"streamlined version",
|
|
123
|
+
"skip audit",
|
|
124
|
+
"skip the audit",
|
|
125
|
+
"quick version",
|
|
126
|
+
"to save time",
|
|
127
|
+
"custom scoring",
|
|
128
|
+
"too heavyweight",
|
|
129
|
+
"lightweight audit",
|
|
130
|
+
"simplified protocol",
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
# Per-check point weights (sum = 100)
|
|
134
|
+
_WEIGHTS = {
|
|
135
|
+
"three_laws": 25,
|
|
136
|
+
"lmc_protocol": 15,
|
|
137
|
+
"done_json": 20,
|
|
138
|
+
"done_marker": 10,
|
|
139
|
+
"scope": 15,
|
|
140
|
+
"fresh_context": 10,
|
|
141
|
+
"no_banned": 5,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
# Helpers
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _short_evidence(text: str, match: re.Match) -> str:
|
|
151
|
+
"""Return a short snippet (~60 chars) around the match for the report."""
|
|
152
|
+
start = max(0, match.start() - 10)
|
|
153
|
+
end = min(len(text), match.end() + 20)
|
|
154
|
+
snippet = text[start:end].replace("\n", " ").strip()
|
|
155
|
+
if len(snippet) > 80:
|
|
156
|
+
snippet = snippet[:77] + "..."
|
|
157
|
+
return snippet
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _check_pattern(
|
|
161
|
+
text: str,
|
|
162
|
+
pattern: re.Pattern,
|
|
163
|
+
name: str,
|
|
164
|
+
points: int,
|
|
165
|
+
) -> CheckResult:
|
|
166
|
+
"""Run a regex check and return a CheckResult."""
|
|
167
|
+
m = pattern.search(text)
|
|
168
|
+
if m:
|
|
169
|
+
return CheckResult(passed=True, evidence=_short_evidence(text, m),
|
|
170
|
+
points=points)
|
|
171
|
+
return CheckResult(passed=False, evidence=None, points=0)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _check_no_banned(text: str, points: int) -> CheckResult:
|
|
175
|
+
"""Pass iff no banned phrase appears. The phrase list is short and
|
|
176
|
+
targets action-oriented patterns (e.g. 'streamlined approach') rather
|
|
177
|
+
than descriptive ones (e.g. bare 'lightweight') to avoid false
|
|
178
|
+
positives in legitimate architecture descriptions."""
|
|
179
|
+
lowered = text.lower()
|
|
180
|
+
for phrase in _BANNED_PHRASES:
|
|
181
|
+
if phrase in lowered:
|
|
182
|
+
return CheckResult(passed=False, evidence=phrase, points=0)
|
|
183
|
+
return CheckResult(passed=True, evidence=None, points=points)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _agent_id_from_path(path: Path) -> str:
|
|
187
|
+
"""Derive a short agent id from the file path (the .stem)."""
|
|
188
|
+
return path.stem
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ---------------------------------------------------------------------------
|
|
192
|
+
# Public API — single file
|
|
193
|
+
# ---------------------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def audit_agent_prompt(path: Path) -> AgentPromptReport:
|
|
197
|
+
"""Score one agent prompt .md file on the seven contract checks.
|
|
198
|
+
|
|
199
|
+
Scoring (sum = 100):
|
|
200
|
+
- Three Laws reference 25 pts
|
|
201
|
+
- LMC protocol reference 15 pts
|
|
202
|
+
- `.done.json` reference 20 pts
|
|
203
|
+
- Done-marker tooling reference 10 pts
|
|
204
|
+
- Scope / files_owned / responsibilities 15 pts
|
|
205
|
+
- Fresh context / self-contained brief 10 pts
|
|
206
|
+
- No banned phrases (rule 46-no-time-panic) 5 pts
|
|
207
|
+
|
|
208
|
+
Returns a fully-populated :class:`AgentPromptReport`. If the file does
|
|
209
|
+
not exist or can't be read, returns a report with score=0 and a single
|
|
210
|
+
violation explaining why.
|
|
211
|
+
"""
|
|
212
|
+
path = Path(path)
|
|
213
|
+
agent_id = _agent_id_from_path(path)
|
|
214
|
+
report = AgentPromptReport(agent_id=agent_id, file_path=str(path))
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
text = path.read_text(encoding="utf-8")
|
|
218
|
+
except (OSError, UnicodeDecodeError) as exc:
|
|
219
|
+
report.violations.append(f"unreadable file: {exc}")
|
|
220
|
+
return report
|
|
221
|
+
|
|
222
|
+
checks: dict[str, CheckResult] = {
|
|
223
|
+
"three_laws": _check_pattern(text, _RE_THREE_LAWS,
|
|
224
|
+
"three_laws",
|
|
225
|
+
_WEIGHTS["three_laws"]),
|
|
226
|
+
"lmc_protocol": _check_pattern(text, _RE_LMC,
|
|
227
|
+
"lmc_protocol",
|
|
228
|
+
_WEIGHTS["lmc_protocol"]),
|
|
229
|
+
"done_json": _check_pattern(text, _RE_DONE_JSON,
|
|
230
|
+
"done_json",
|
|
231
|
+
_WEIGHTS["done_json"]),
|
|
232
|
+
"done_marker": _check_pattern(text, _RE_DONE_MARKER,
|
|
233
|
+
"done_marker",
|
|
234
|
+
_WEIGHTS["done_marker"]),
|
|
235
|
+
"scope": _check_pattern(text, _RE_SCOPE,
|
|
236
|
+
"scope",
|
|
237
|
+
_WEIGHTS["scope"]),
|
|
238
|
+
"fresh_context": _check_pattern(text, _RE_FRESH_CONTEXT,
|
|
239
|
+
"fresh_context",
|
|
240
|
+
_WEIGHTS["fresh_context"]),
|
|
241
|
+
"no_banned": _check_no_banned(text, _WEIGHTS["no_banned"]),
|
|
242
|
+
}
|
|
243
|
+
report.checks = checks
|
|
244
|
+
|
|
245
|
+
# Compute score and violations.
|
|
246
|
+
score = sum(c.points for c in checks.values())
|
|
247
|
+
report.score = min(100, score)
|
|
248
|
+
|
|
249
|
+
# Human-readable violations for the doctor line summary.
|
|
250
|
+
human = {
|
|
251
|
+
"three_laws": "Three Laws",
|
|
252
|
+
"lmc_protocol": "LMC protocol",
|
|
253
|
+
"done_json": "`.done.json` contract",
|
|
254
|
+
"done_marker": "done-marker tooling",
|
|
255
|
+
"scope": "scope/responsibilities",
|
|
256
|
+
"fresh_context": "fresh context handoff",
|
|
257
|
+
"no_banned": "banned phrase present",
|
|
258
|
+
}
|
|
259
|
+
for name, res in checks.items():
|
|
260
|
+
if not res.passed:
|
|
261
|
+
label = human[name]
|
|
262
|
+
if name == "no_banned" and res.evidence:
|
|
263
|
+
label = f"{label} ({res.evidence})"
|
|
264
|
+
report.violations.append(label)
|
|
265
|
+
|
|
266
|
+
return report
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# ---------------------------------------------------------------------------
|
|
270
|
+
# Public API — full suite
|
|
271
|
+
# ---------------------------------------------------------------------------
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _aisb_dir(omega_home: Path) -> Path:
|
|
275
|
+
return Path(omega_home) / "Agentik_SSOT" / "agents" / "aisb"
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _iter_aisb_prompts(omega_home: Path) -> list[Path]:
|
|
279
|
+
"""Return every .md directly under aisb/ (subdirs excluded)."""
|
|
280
|
+
aisb = _aisb_dir(omega_home)
|
|
281
|
+
if not aisb.is_dir():
|
|
282
|
+
return []
|
|
283
|
+
return sorted(p for p in aisb.iterdir()
|
|
284
|
+
if p.is_file() and p.suffix == ".md")
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def audit_aisb_suite(omega_home: Path) -> SuiteReport:
|
|
288
|
+
"""Audit every .md directly under ``aisb/`` (subdirs excluded).
|
|
289
|
+
|
|
290
|
+
Returns a :class:`SuiteReport`. If no prompts are found, ``per_agent``
|
|
291
|
+
is an empty list and downstream callers should warn (the install
|
|
292
|
+
step 25-aisb-suite probably was skipped).
|
|
293
|
+
"""
|
|
294
|
+
omega_home = Path(omega_home)
|
|
295
|
+
paths = _iter_aisb_prompts(omega_home)
|
|
296
|
+
per_agent = [audit_agent_prompt(p) for p in paths]
|
|
297
|
+
|
|
298
|
+
if per_agent:
|
|
299
|
+
avg = sum(r.score for r in per_agent) / len(per_agent)
|
|
300
|
+
else:
|
|
301
|
+
avg = 0.0
|
|
302
|
+
|
|
303
|
+
missing_critical = sorted(r.agent_id for r in per_agent if r.score < 60)
|
|
304
|
+
|
|
305
|
+
return SuiteReport(
|
|
306
|
+
per_agent=per_agent,
|
|
307
|
+
average_score=avg,
|
|
308
|
+
missing_critical=missing_critical,
|
|
309
|
+
orchestration_chain_intact=_chain_intact(per_agent),
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _chain_intact(reports: list[AgentPromptReport]) -> bool:
|
|
314
|
+
"""The AISB → Oracle → Worker chain is intact when:
|
|
315
|
+
- each of the three roles exists in the suite, AND
|
|
316
|
+
- all three reference `.done.json` (the shared completion vocabulary).
|
|
317
|
+
|
|
318
|
+
'Worker' is taken loosely: morpheus (the canonical executor) or
|
|
319
|
+
construct (alias target for the generic 'worker' role) satisfies the
|
|
320
|
+
worker slot. This matches the alias map in ``prompts.py``.
|
|
321
|
+
"""
|
|
322
|
+
by_id = {r.agent_id: r for r in reports}
|
|
323
|
+
aisb_master = by_id.get("CLAUDE") # the master prompt of the AISB suite
|
|
324
|
+
oracle = by_id.get("oracle")
|
|
325
|
+
worker = by_id.get("morpheus") or by_id.get("construct")
|
|
326
|
+
|
|
327
|
+
if not (aisb_master and oracle and worker):
|
|
328
|
+
return False
|
|
329
|
+
return all(r.checks.get("done_json", CheckResult(passed=False)).passed
|
|
330
|
+
for r in (aisb_master, oracle, worker))
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# ---------------------------------------------------------------------------
|
|
334
|
+
# Public API — orchestration health
|
|
335
|
+
# ---------------------------------------------------------------------------
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def orchestration_health(omega_home: Path) -> dict:
|
|
339
|
+
"""Higher-level check: is the AISB → Oracle → Worker → Checker chain
|
|
340
|
+
wired and does the suite share the verified-completion vocabulary?
|
|
341
|
+
|
|
342
|
+
Returns a dict with:
|
|
343
|
+
- ``aisb_master_present`` — CLAUDE.md exists in aisb/
|
|
344
|
+
- ``oracle_present`` — oracle.md exists
|
|
345
|
+
- ``workers_role_present`` — morpheus.md OR construct.md exists
|
|
346
|
+
- ``checker_present`` — seraph.md OR smith.md exists
|
|
347
|
+
- ``lmc_protocol_present`` — lmc-protocol.md exists
|
|
348
|
+
- ``shared_vocab_overlap`` — fraction (0.0..1.0) of agents
|
|
349
|
+
mentioning `.done.json` in their file
|
|
350
|
+
"""
|
|
351
|
+
omega_home = Path(omega_home)
|
|
352
|
+
aisb = _aisb_dir(omega_home)
|
|
353
|
+
|
|
354
|
+
def has(name: str) -> bool:
|
|
355
|
+
return (aisb / name).is_file()
|
|
356
|
+
|
|
357
|
+
out: dict = {
|
|
358
|
+
"aisb_master_present": has("CLAUDE.md"),
|
|
359
|
+
"oracle_present": has("oracle.md"),
|
|
360
|
+
"workers_role_present": has("morpheus.md") or has("construct.md"),
|
|
361
|
+
"checker_present": has("seraph.md") or has("smith.md"),
|
|
362
|
+
"lmc_protocol_present": has("lmc-protocol.md"),
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
# Shared vocabulary overlap — fraction of agents that reference
|
|
366
|
+
# `.done.json` in their file. A high overlap means the completion
|
|
367
|
+
# contract is part of the muscle memory of the suite; a low overlap
|
|
368
|
+
# means some agents are silent on the done signal and may complete
|
|
369
|
+
# tasks without writing the structured result downstream consumers
|
|
370
|
+
# expect.
|
|
371
|
+
paths = _iter_aisb_prompts(omega_home)
|
|
372
|
+
if not paths:
|
|
373
|
+
out["shared_vocab_overlap"] = 0.0
|
|
374
|
+
return out
|
|
375
|
+
|
|
376
|
+
matches = 0
|
|
377
|
+
for p in paths:
|
|
378
|
+
try:
|
|
379
|
+
text = p.read_text(encoding="utf-8")
|
|
380
|
+
except (OSError, UnicodeDecodeError):
|
|
381
|
+
continue
|
|
382
|
+
if _RE_DONE_JSON.search(text):
|
|
383
|
+
matches += 1
|
|
384
|
+
out["shared_vocab_overlap"] = matches / len(paths)
|
|
385
|
+
return out
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
__all__ = [
|
|
389
|
+
"CheckResult",
|
|
390
|
+
"AgentPromptReport",
|
|
391
|
+
"SuiteReport",
|
|
392
|
+
"audit_agent_prompt",
|
|
393
|
+
"audit_aisb_suite",
|
|
394
|
+
"orchestration_health",
|
|
395
|
+
]
|
|
@@ -325,6 +325,22 @@ def _spawn_with_shell_then_run(
|
|
|
325
325
|
return name
|
|
326
326
|
|
|
327
327
|
|
|
328
|
+
def omega_window_alive(window_name: str) -> bool:
|
|
329
|
+
"""True if a window named ``window_name`` exists inside the Omega
|
|
330
|
+
master tmux session.
|
|
331
|
+
|
|
332
|
+
Used by the TUI chat-list panel to render ● (alive) vs ○ (off) next
|
|
333
|
+
to AISB-chat / Hermès-chat. Cheap — one ``tmux list-windows`` call;
|
|
334
|
+
returns False on any error including 'no Omega session'.
|
|
335
|
+
"""
|
|
336
|
+
if not is_alive("Omega"):
|
|
337
|
+
return False
|
|
338
|
+
rc, out = _tmux("list-windows", "-t", "Omega", "-F", "#W")
|
|
339
|
+
if rc != 0:
|
|
340
|
+
return False
|
|
341
|
+
return window_name in (out or "").splitlines()
|
|
342
|
+
|
|
343
|
+
|
|
328
344
|
def spawn_chat_in_omega(
|
|
329
345
|
window_name: str,
|
|
330
346
|
*,
|