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.
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=self._detect_session_type(session_file),
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=self._detect_session_type(session_file),
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 agent_id and session_id:
227
+ if session_id:
228
228
  try:
229
- self.db.update_session_agent_id(session_id, agent_id)
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: