aline-ai 0.6.6__py3-none-any.whl → 0.7.0__py3-none-any.whl
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.
- {aline_ai-0.6.6.dist-info → aline_ai-0.7.0.dist-info}/METADATA +1 -1
- {aline_ai-0.6.6.dist-info → aline_ai-0.7.0.dist-info}/RECORD +28 -28
- realign/__init__.py +1 -1
- realign/agent_names.py +2 -2
- realign/claude_hooks/terminal_state.py +32 -1
- realign/cli.py +2 -4
- realign/codex_detector.py +17 -2
- realign/codex_home.py +24 -6
- realign/commands/auth.py +20 -0
- realign/commands/doctor.py +74 -1
- realign/commands/export_shares.py +151 -0
- realign/commands/import_shares.py +203 -1
- realign/commands/sync_agent.py +347 -0
- realign/dashboard/app.py +3 -53
- realign/dashboard/screens/create_agent_info.py +131 -20
- realign/dashboard/styles/dashboard.tcss +0 -73
- realign/dashboard/tmux_manager.py +36 -10
- realign/dashboard/widgets/__init__.py +0 -6
- realign/dashboard/widgets/agents_panel.py +157 -24
- realign/db/base.py +43 -1
- realign/db/schema.py +60 -2
- realign/db/sqlite_db.py +176 -1
- realign/watcher_core.py +133 -2
- realign/worker_core.py +37 -2
- realign/dashboard/widgets/terminal_panel.py +0 -1688
- {aline_ai-0.6.6.dist-info → aline_ai-0.7.0.dist-info}/WHEEL +0 -0
- {aline_ai-0.6.6.dist-info → aline_ai-0.7.0.dist-info}/entry_points.txt +0 -0
- {aline_ai-0.6.6.dist-info → aline_ai-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {aline_ai-0.6.6.dist-info → aline_ai-0.7.0.dist-info}/top_level.txt +0 -0
realign/watcher_core.py
CHANGED
|
@@ -259,6 +259,54 @@ class DialogueWatcher:
|
|
|
259
259
|
except Exception:
|
|
260
260
|
return
|
|
261
261
|
|
|
262
|
+
def _agent_info_id_for_codex_session(
|
|
263
|
+
self, session_file: Path, *, db=None
|
|
264
|
+
) -> Optional[str]:
|
|
265
|
+
"""Best-effort: resolve agent_info_id from a codex session file."""
|
|
266
|
+
try:
|
|
267
|
+
from .codex_home import codex_home_owner_from_session_file
|
|
268
|
+
except Exception:
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
owner = codex_home_owner_from_session_file(session_file)
|
|
273
|
+
except Exception:
|
|
274
|
+
owner = None
|
|
275
|
+
if not owner or owner[0] != "terminal":
|
|
276
|
+
return None
|
|
277
|
+
terminal_id = owner[1]
|
|
278
|
+
if not terminal_id:
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
if db is None:
|
|
283
|
+
from .db import get_database
|
|
284
|
+
|
|
285
|
+
db = get_database(read_only=True)
|
|
286
|
+
agent = db.get_agent_by_id(terminal_id)
|
|
287
|
+
except Exception:
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
source = (agent.source or "").strip() if agent else ""
|
|
291
|
+
if source.startswith("agent:"):
|
|
292
|
+
return source[6:]
|
|
293
|
+
return None
|
|
294
|
+
|
|
295
|
+
def _terminal_id_for_codex_session(self, session_file: Path) -> Optional[str]:
|
|
296
|
+
"""Best-effort: resolve terminal_id from codex session file path."""
|
|
297
|
+
try:
|
|
298
|
+
from .codex_home import codex_home_owner_from_session_file
|
|
299
|
+
except Exception:
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
owner = codex_home_owner_from_session_file(session_file)
|
|
304
|
+
except Exception:
|
|
305
|
+
owner = None
|
|
306
|
+
if not owner or owner[0] != "terminal":
|
|
307
|
+
return None
|
|
308
|
+
return owner[1]
|
|
309
|
+
|
|
262
310
|
try:
|
|
263
311
|
from .codex_home import codex_home_owner_from_session_file
|
|
264
312
|
from .codex_terminal_linker import read_codex_session_meta, select_agent_for_codex_session
|
|
@@ -326,6 +374,18 @@ class DialogueWatcher:
|
|
|
326
374
|
source=source,
|
|
327
375
|
)
|
|
328
376
|
|
|
377
|
+
try:
|
|
378
|
+
db.insert_window_link(
|
|
379
|
+
terminal_id=agent_id,
|
|
380
|
+
agent_id=agent_info_id,
|
|
381
|
+
session_id=session_file.stem,
|
|
382
|
+
provider="codex",
|
|
383
|
+
source="codex:watcher",
|
|
384
|
+
ts=time.time(),
|
|
385
|
+
)
|
|
386
|
+
except Exception:
|
|
387
|
+
pass
|
|
388
|
+
|
|
329
389
|
# Link session to agent_info if available (bidirectional linking)
|
|
330
390
|
if agent_info_id:
|
|
331
391
|
try:
|
|
@@ -851,6 +911,23 @@ class DialogueWatcher:
|
|
|
851
911
|
file=sys.stderr,
|
|
852
912
|
)
|
|
853
913
|
|
|
914
|
+
agent_id = None
|
|
915
|
+
if session_type == "codex":
|
|
916
|
+
agent_id = self._agent_info_id_for_codex_session(session_file, db=db)
|
|
917
|
+
terminal_id = self._terminal_id_for_codex_session(session_file)
|
|
918
|
+
if terminal_id:
|
|
919
|
+
try:
|
|
920
|
+
db.insert_window_link(
|
|
921
|
+
terminal_id=terminal_id,
|
|
922
|
+
agent_id=agent_id,
|
|
923
|
+
session_id=session_id,
|
|
924
|
+
provider="codex",
|
|
925
|
+
source="codex:watcher",
|
|
926
|
+
ts=time.time(),
|
|
927
|
+
)
|
|
928
|
+
except Exception:
|
|
929
|
+
pass
|
|
930
|
+
|
|
854
931
|
enqueued = 0
|
|
855
932
|
for turn in missing_turns:
|
|
856
933
|
try:
|
|
@@ -859,6 +936,7 @@ class DialogueWatcher:
|
|
|
859
936
|
workspace_path=project_path,
|
|
860
937
|
turn_number=turn,
|
|
861
938
|
session_type=session_type,
|
|
939
|
+
agent_id=agent_id if agent_id else None,
|
|
862
940
|
)
|
|
863
941
|
enqueued += 1
|
|
864
942
|
except Exception as e:
|
|
@@ -992,6 +1070,22 @@ class DialogueWatcher:
|
|
|
992
1070
|
continue
|
|
993
1071
|
|
|
994
1072
|
enqueued_any = False
|
|
1073
|
+
agent_id = None
|
|
1074
|
+
if session_type == "codex":
|
|
1075
|
+
agent_id = self._agent_info_id_for_codex_session(session_file, db=db)
|
|
1076
|
+
terminal_id = self._terminal_id_for_codex_session(session_file)
|
|
1077
|
+
if terminal_id:
|
|
1078
|
+
try:
|
|
1079
|
+
db.insert_window_link(
|
|
1080
|
+
terminal_id=terminal_id,
|
|
1081
|
+
agent_id=agent_id,
|
|
1082
|
+
session_id=session_id,
|
|
1083
|
+
provider="codex",
|
|
1084
|
+
source="codex:watcher",
|
|
1085
|
+
ts=time.time(),
|
|
1086
|
+
)
|
|
1087
|
+
except Exception:
|
|
1088
|
+
pass
|
|
995
1089
|
for turn_number in sorted(set(new_turns)):
|
|
996
1090
|
try:
|
|
997
1091
|
db.enqueue_turn_summary_job( # type: ignore[attr-defined]
|
|
@@ -999,6 +1093,7 @@ class DialogueWatcher:
|
|
|
999
1093
|
workspace_path=project_path,
|
|
1000
1094
|
turn_number=int(turn_number),
|
|
1001
1095
|
session_type=session_type,
|
|
1096
|
+
agent_id=agent_id if agent_id else None,
|
|
1002
1097
|
)
|
|
1003
1098
|
enqueued_any = True
|
|
1004
1099
|
except Exception as e:
|
|
@@ -1249,13 +1344,31 @@ class DialogueWatcher:
|
|
|
1249
1344
|
continue
|
|
1250
1345
|
|
|
1251
1346
|
enqueued_any = False
|
|
1347
|
+
session_type = self._detect_session_type(session_file)
|
|
1348
|
+
agent_id = None
|
|
1349
|
+
if session_type == "codex":
|
|
1350
|
+
agent_id = self._agent_info_id_for_codex_session(session_file, db=db)
|
|
1351
|
+
terminal_id = self._terminal_id_for_codex_session(session_file)
|
|
1352
|
+
if terminal_id:
|
|
1353
|
+
try:
|
|
1354
|
+
db.insert_window_link(
|
|
1355
|
+
terminal_id=terminal_id,
|
|
1356
|
+
agent_id=agent_id,
|
|
1357
|
+
session_id=session_file.stem,
|
|
1358
|
+
provider="codex",
|
|
1359
|
+
source="codex:watcher",
|
|
1360
|
+
ts=time.time(),
|
|
1361
|
+
)
|
|
1362
|
+
except Exception:
|
|
1363
|
+
pass
|
|
1252
1364
|
for turn_number in new_turns:
|
|
1253
1365
|
try:
|
|
1254
1366
|
db.enqueue_turn_summary_job( # type: ignore[attr-defined]
|
|
1255
1367
|
session_file_path=session_file,
|
|
1256
1368
|
workspace_path=project_path,
|
|
1257
1369
|
turn_number=turn_number,
|
|
1258
|
-
session_type=
|
|
1370
|
+
session_type=session_type,
|
|
1371
|
+
agent_id=agent_id if agent_id else None,
|
|
1259
1372
|
)
|
|
1260
1373
|
enqueued_any = True
|
|
1261
1374
|
except Exception as e:
|
|
@@ -1305,11 +1418,29 @@ class DialogueWatcher:
|
|
|
1305
1418
|
|
|
1306
1419
|
for turn_number in new_turns:
|
|
1307
1420
|
try:
|
|
1421
|
+
session_type = self._detect_session_type(session_file)
|
|
1422
|
+
agent_id = None
|
|
1423
|
+
if session_type == "codex":
|
|
1424
|
+
agent_id = self._agent_info_id_for_codex_session(session_file, db=db)
|
|
1425
|
+
terminal_id = self._terminal_id_for_codex_session(session_file)
|
|
1426
|
+
if terminal_id:
|
|
1427
|
+
try:
|
|
1428
|
+
db.insert_window_link(
|
|
1429
|
+
terminal_id=terminal_id,
|
|
1430
|
+
agent_id=agent_id,
|
|
1431
|
+
session_id=session_file.stem,
|
|
1432
|
+
provider="codex",
|
|
1433
|
+
source="codex:watcher",
|
|
1434
|
+
ts=time.time(),
|
|
1435
|
+
)
|
|
1436
|
+
except Exception:
|
|
1437
|
+
pass
|
|
1308
1438
|
db.enqueue_turn_summary_job( # type: ignore[attr-defined]
|
|
1309
1439
|
session_file_path=session_file,
|
|
1310
1440
|
workspace_path=project_path,
|
|
1311
1441
|
turn_number=turn_number,
|
|
1312
|
-
session_type=
|
|
1442
|
+
session_type=session_type,
|
|
1443
|
+
agent_id=agent_id if agent_id else None,
|
|
1313
1444
|
)
|
|
1314
1445
|
except Exception:
|
|
1315
1446
|
continue
|
realign/worker_core.py
CHANGED
|
@@ -224,9 +224,12 @@ class AlineWorker:
|
|
|
224
224
|
)
|
|
225
225
|
|
|
226
226
|
# Link session to agent after commit ensures session exists in DB
|
|
227
|
-
if
|
|
227
|
+
if session_id:
|
|
228
228
|
try:
|
|
229
|
-
|
|
229
|
+
if agent_id:
|
|
230
|
+
self.db.update_session_agent_id(session_id, agent_id)
|
|
231
|
+
else:
|
|
232
|
+
self._maybe_link_session_from_terminal(session_id)
|
|
230
233
|
except Exception:
|
|
231
234
|
pass
|
|
232
235
|
|
|
@@ -261,6 +264,38 @@ class AlineWorker:
|
|
|
261
264
|
except Exception as e:
|
|
262
265
|
logger.warning(f"Failed to enqueue session summary after import for {session_id}: {e}")
|
|
263
266
|
|
|
267
|
+
def _maybe_link_session_from_terminal(self, session_id: str) -> None:
|
|
268
|
+
"""Best-effort: backfill sessions.agent_id using terminal mapping."""
|
|
269
|
+
try:
|
|
270
|
+
session = self.db.get_session_by_id(session_id)
|
|
271
|
+
if session and getattr(session, "agent_id", None):
|
|
272
|
+
return
|
|
273
|
+
except Exception:
|
|
274
|
+
return
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
agents = self.db.list_agents(status=None, limit=1000)
|
|
278
|
+
except Exception:
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
agent_info_id = None
|
|
282
|
+
for agent in agents:
|
|
283
|
+
try:
|
|
284
|
+
if (agent.session_id or "").strip() != session_id:
|
|
285
|
+
continue
|
|
286
|
+
source = (agent.source or "").strip()
|
|
287
|
+
if source.startswith("agent:"):
|
|
288
|
+
agent_info_id = source[6:]
|
|
289
|
+
break
|
|
290
|
+
except Exception:
|
|
291
|
+
continue
|
|
292
|
+
|
|
293
|
+
if agent_info_id:
|
|
294
|
+
try:
|
|
295
|
+
self.db.update_session_agent_id(session_id, agent_info_id)
|
|
296
|
+
except Exception:
|
|
297
|
+
pass
|
|
298
|
+
|
|
264
299
|
async def _process_session_summary_job(self, payload: Dict[str, Any]) -> bool:
|
|
265
300
|
session_id = str(payload.get("session_id") or "")
|
|
266
301
|
if not session_id:
|