aline-ai 0.6.5__py3-none-any.whl → 0.6.6__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.
Files changed (38) hide show
  1. {aline_ai-0.6.5.dist-info → aline_ai-0.6.6.dist-info}/METADATA +1 -1
  2. {aline_ai-0.6.5.dist-info → aline_ai-0.6.6.dist-info}/RECORD +38 -31
  3. realign/__init__.py +1 -1
  4. realign/agent_names.py +79 -0
  5. realign/claude_hooks/stop_hook.py +3 -0
  6. realign/claude_hooks/terminal_state.py +11 -0
  7. realign/claude_hooks/user_prompt_submit_hook.py +3 -0
  8. realign/cli.py +62 -0
  9. realign/codex_detector.py +1 -1
  10. realign/codex_home.py +46 -15
  11. realign/codex_terminal_linker.py +18 -7
  12. realign/commands/agent.py +109 -0
  13. realign/commands/export_shares.py +297 -0
  14. realign/commands/search.py +58 -29
  15. realign/dashboard/app.py +9 -9
  16. realign/dashboard/clipboard.py +54 -0
  17. realign/dashboard/screens/__init__.py +4 -0
  18. realign/dashboard/screens/agent_detail.py +333 -0
  19. realign/dashboard/screens/create_agent_info.py +133 -0
  20. realign/dashboard/screens/event_detail.py +6 -27
  21. realign/dashboard/styles/dashboard.tcss +67 -0
  22. realign/dashboard/widgets/__init__.py +2 -0
  23. realign/dashboard/widgets/agents_panel.py +1129 -0
  24. realign/dashboard/widgets/events_table.py +4 -27
  25. realign/dashboard/widgets/sessions_table.py +4 -27
  26. realign/dashboard/widgets/terminal_panel.py +40 -5
  27. realign/db/base.py +27 -0
  28. realign/db/locks.py +4 -0
  29. realign/db/schema.py +53 -2
  30. realign/db/sqlite_db.py +185 -2
  31. realign/events/agent_summarizer.py +157 -0
  32. realign/events/session_summarizer.py +25 -0
  33. realign/watcher_core.py +60 -3
  34. realign/worker_core.py +24 -1
  35. {aline_ai-0.6.5.dist-info → aline_ai-0.6.6.dist-info}/WHEEL +0 -0
  36. {aline_ai-0.6.5.dist-info → aline_ai-0.6.6.dist-info}/entry_points.txt +0 -0
  37. {aline_ai-0.6.5.dist-info → aline_ai-0.6.6.dist-info}/licenses/LICENSE +0 -0
  38. {aline_ai-0.6.5.dist-info → aline_ai-0.6.6.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aline-ai
3
- Version: 0.6.5
3
+ Version: 0.6.6
4
4
  Summary: Shared AI memory; everyone knows everything in teams
5
5
  Author: Sharemind
6
6
  License: MIT
@@ -1,11 +1,12 @@
1
- aline_ai-0.6.5.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
2
- realign/__init__.py,sha256=02FiDcPQx1TGbGJO98rtDO7k-JAA9WrZKtygoavnEY8,1623
1
+ aline_ai-0.6.6.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
2
+ realign/__init__.py,sha256=0jG4gYJd6Zmf7o7WaFAwx0d2VJ0amHPKJ8u3BqkhG6A,1623
3
+ realign/agent_names.py,sha256=ml6Jc1B_nRRr-FcW1tIFMyqxw-90RBLcfzj6fwJRB4U,1158
3
4
  realign/auth.py,sha256=d_1yvCwluN5iIrdgjtuSKpOYAksDzrzNgntKacLVJrw,16583
4
5
  realign/claude_detector.py,sha256=ZLSJacMo6zzQclXByABKA70UNpstxqIv3fPGqdpA934,2792
5
- realign/cli.py,sha256=HZ_1Rm50z1oszCwvPAZcAdPt0Gl-dj0S0NMLy2sWu_4,35665
6
- realign/codex_detector.py,sha256=xTpYgMfUwL6UL76xeHl8xF2ZBPHdjwmgXmbmZkvHA0A,5523
7
- realign/codex_home.py,sha256=gAAosBDru4jfz0QCn12A2bZEC_lZxytpOAzk7GOXTpI,2512
8
- realign/codex_terminal_linker.py,sha256=9cDUHhN7MhCIUOfb-3kApPY-l6s91jq2qq_WI0ccexY,5926
6
+ realign/cli.py,sha256=IctmQ0OTb6kLlWRFRQumdhY6-CpcpFtocdc68KiwxvM,37748
7
+ realign/codex_detector.py,sha256=t_34CkvxP4x9CxuHmbk8YhEhu8RKLRG-CDr_FaRMQYY,5536
8
+ realign/codex_home.py,sha256=9cfO_kF-WYKAJXdSnceTB1PaRPBpZjAWyDtO0_U_BvA,3569
9
+ realign/codex_terminal_linker.py,sha256=L2Ha4drlZ7Sbq2jzXyxczOdUY3S5fu1gJqoI5WN9CKk,6211
9
10
  realign/config.py,sha256=Znfs43AjiK90LGWnArDPWyrE859sdZQAPIb0KAcU3Ig,9252
10
11
  realign/context.py,sha256=8hzgNOg-7_eMW22wt7OM5H9IsmMveKXCv0epG7E0G7w,13917
11
12
  realign/file_lock.py,sha256=kLNm1Rra4TCrTMyPM5fwjVascq-CUz2Bzh9HHKtCKOE,3444
@@ -15,9 +16,9 @@ realign/logging_config.py,sha256=LCAigKFhTj86PSJm4-kUl3Ag9h_GENh3x2iPnMv7qUI,487
15
16
  realign/mcp_server.py,sha256=LWiQ2qukYoNLsoV2ID2f0vF9jkJlBvB587HpM5jymgE,10193
16
17
  realign/mcp_watcher.py,sha256=aK4jWStv7CoCroS4tXFHgZ_y_-q4QDjrpWgm4DxcEj4,1260
17
18
  realign/redactor.py,sha256=Zsoi5HfYak2yPmck20JArhm-1cPSB78IdkBJiNVXfrc,17096
18
- realign/watcher_core.py,sha256=0XCoA5giuie-Ytc_tlPTVbZH8EFRPmODu7DUYRhRBGo,108598
19
+ realign/watcher_core.py,sha256=CeHY6PN8vzn4cNezjCWNr3yLEuNsRsshfI5W3HfsXQk,110978
19
20
  realign/watcher_daemon.py,sha256=OHUQ9P1LlagKJHfrf6uRnzO-zDtBRXIxt8ydMFHf5S8,3475
20
- realign/worker_core.py,sha256=TXioUVJlOO-8EgmKssCTLIyuh0aaupRLb1sh9s3kSuc,10194
21
+ realign/worker_core.py,sha256=TrFvqlElpa3Vnz7yumVfYaw4h9GcOLhuLhumPkeNCTg,11195
21
22
  realign/worker_daemon.py,sha256=X7Xyjw_u6m6KG4E84nx0HpDFw4cWMv8ja1G8btc9PiM,3957
22
23
  realign/adapters/__init__.py,sha256=alkJr7DRn_CrJecSJRjRJOHHnkz9EnZ5TnsU8n1Bb0k,719
23
24
  realign/adapters/base.py,sha256=2IdAZKGjg5gPB3YLf_8r3V4XAdbK7fHpj06GjjsYEFY,7409
@@ -28,62 +29,68 @@ realign/adapters/registry.py,sha256=yM6nf9nGTJ1vaK2Uixp-VacseK7PmxZkCdKedmWI8MA,
28
29
  realign/claude_hooks/__init__.py,sha256=MT9c8TWjLO23xDCM-uBBMy_mOThNd7O-AgN_Khn30qs,594
29
30
  realign/claude_hooks/permission_request_hook.py,sha256=jMN7UtL6bMqHObUCP5A5ysvFrooDEcd9KxtmF2-3nCw,6448
30
31
  realign/claude_hooks/permission_request_hook_installer.py,sha256=_8Wr_L5MES7iGukJzcaj4bqR0BH8kFL44U_X4iKtw2Y,7791
31
- realign/claude_hooks/stop_hook.py,sha256=GWADlzaTGzV8_BUKLLGhHmwJDIXSLQGVUUBuP_rdJ0o,13431
32
+ realign/claude_hooks/stop_hook.py,sha256=Bzf6CjHQ-0q61SrDrpIvcwt_BmDO1FE-f8cws_aA-Is,13582
32
33
  realign/claude_hooks/stop_hook_installer.py,sha256=uyqKOqpix7CQP64ERBvvh7viSPp_wx_JVGNAX18rKh0,7228
33
- realign/claude_hooks/terminal_state.py,sha256=i8B6b_2_9ttPEemp7SrGdFRJSa-vm5lc7YSTRTvAWNg,5397
34
- realign/claude_hooks/user_prompt_submit_hook.py,sha256=kMrmhAVtfV41oTX7JZcq2HPXjgQQ5gX26iOJoHJkfqA,10474
34
+ realign/claude_hooks/terminal_state.py,sha256=Ywl173lD9-eUTXvYFaCczkQXNIqzzKL1zXAJPNpMiW8,5728
35
+ realign/claude_hooks/user_prompt_submit_hook.py,sha256=8e0zNonT95TH2uuISYp3am_RD7c84Ghh1WRPgs023DI,10625
35
36
  realign/claude_hooks/user_prompt_submit_hook_installer.py,sha256=2xLF8yZcE7Iwib9gU-xCkA1NWxNH9Nc5CFKPYK7rtXw,5371
36
37
  realign/commands/__init__.py,sha256=WVaVT1orM2Z0PYaG3X6tkKb_t2v3n_3siCadh1qd_QA,107
37
38
  realign/commands/add.py,sha256=_Xzt9P15mwndA3JvBBVrki8tn9Cc0UP6SiLwM4RS8Nc,27232
39
+ realign/commands/agent.py,sha256=3CS48bMn7tkdDWKRrfg7CYbhcJK4Pz40YjYMvwD7c2w,3173
38
40
  realign/commands/auth.py,sha256=QrPukpP-ogYEDSwztV0NOYI-HDgn5fPxlCQ1-e2n7gU,11082
39
41
  realign/commands/config.py,sha256=nYnu_h2pk7GODcrzrV04K51D-s7v06FlRXHJ0HJ-gvU,6732
40
42
  realign/commands/context.py,sha256=pM2KfZHVkB-ou4nBhFvKSwnYliLBzwN3zerLyBAbhfE,7095
41
43
  realign/commands/doctor.py,sha256=q5UOrUR5Uai4AxgaeOnK1Hig5I5UX7m3Vt00tPnUllg,18289
42
- realign/commands/export_shares.py,sha256=WNOR7FBE2om9qPO_28edZKhs94lyUAcbRgP_kNaDi5M,132574
44
+ realign/commands/export_shares.py,sha256=oQHKSBQKr0PmyjuZibBdO8bmZ1arODRNclws3U9Z0Js,142253
43
45
  realign/commands/import_shares.py,sha256=HiswLlYHqR0dR3wgB7Rs54_WownqahIs5IdyJOHuot8,25572
44
46
  realign/commands/init.py,sha256=6rBr1LVIrQLbUH_UvoDhkF1qXmMh2xkjNWCYAUz5Tho,35274
45
47
  realign/commands/restore.py,sha256=s2BxQZHxQw9r12NzRVsK20KlGafy5AIoSjWMo5PcnHY,11173
46
- realign/commands/search.py,sha256=QJrC0hln9sCDFxXbpo0nPGMHXrud18qA5QfRyD0z6fQ,25926
48
+ realign/commands/search.py,sha256=QlUDzRDD6ebq21LTtLe5-OZM62iwDrDqfbnXbuxfklU,27516
47
49
  realign/commands/upgrade.py,sha256=L3PLOUIN5qAQTbkfoVtSsIbbzEezA_xjjk9F1GMVfjw,12781
48
50
  realign/commands/watcher.py,sha256=4WTThIgr-Z5guKh_JqGDcPmerr97XiHrVaaijmckHsA,134350
49
51
  realign/commands/worker.py,sha256=jTu7Pj60nTnn7SsH3oNCNnO6zl4TIFCJVNSC1OoQ_0o,23363
50
52
  realign/dashboard/__init__.py,sha256=QZkHTsGityH8UkF8rmvA3xW7dMXNe0swEWr443qfgCM,128
51
- realign/dashboard/app.py,sha256=aB1pvuJu-qJ94UqNegB4lvIxUzQJovuC82WQjFnQIFc,10464
53
+ realign/dashboard/app.py,sha256=e257euP0gR9nA0w1susuLkG9tnYQk1IJJdgAICnIYxs,10398
54
+ realign/dashboard/clipboard.py,sha256=81frq83E_urqLkwuCvtl0hiTEjavtdQn8kCi72jJWcs,1207
52
55
  realign/dashboard/layout.py,sha256=sZxmFj6QTbkois9MHTvBEMMcnaRVehCDqugdbiFx10k,9072
53
56
  realign/dashboard/terminal_backend.py,sha256=MlDfwtqhftyQK6jDNizQGFjAWIo5Bx2TDpSnP3MCZVM,3375
54
57
  realign/dashboard/tmux_manager.py,sha256=Fc6OQbnOO4YV47BnrIkcr0SHnQuSFwUSqhepNkpqKLs,32942
55
58
  realign/dashboard/backends/__init__.py,sha256=POROX7YKtukYZcLB1pi_kO0sSEpuO3y-hwmF3WIN1Kk,163
56
59
  realign/dashboard/backends/iterm2.py,sha256=XYYJT5lrrp4pW_MyEqPZYkRI0qyKUwJlezwMidgnsHc,21390
57
60
  realign/dashboard/backends/kitty.py,sha256=5jdkR1f2PwB8a4SnS3EG6uOQ2XU-PB7-cpKBfIJq3hU,12066
58
- realign/dashboard/screens/__init__.py,sha256=US6sAmQs5VVkH2tFkH_z0WDT4H8cVhLL-JckfSR1yQY,446
61
+ realign/dashboard/screens/__init__.py,sha256=MiefFamCYRrzTwQXiCUdybaJaFxlK5XKtLHaSQmqDv0,597
62
+ realign/dashboard/screens/agent_detail.py,sha256=N-iUC4434C91OcDu4dkQaxS_NXQ5Yl5sqNBb2mTmoBw,10490
59
63
  realign/dashboard/screens/create_agent.py,sha256=06uiQYvz-Xvn4Xm689o3tdhzb2HQ0gdzAA1WHVEwziM,11706
64
+ realign/dashboard/screens/create_agent_info.py,sha256=B5rGTb5WPREZPbfopQaXZviXz0lZZHV9l0gmNyGhNII,3914
60
65
  realign/dashboard/screens/create_event.py,sha256=oiQY1zKpUYnQU-5fQLeuZH9BV5NClE5B5XZIVBYG5A8,5506
61
- realign/dashboard/screens/event_detail.py,sha256=Fcm1CPAJkrNzolnFyIAzLeE_-NAoqdjQ0dLW0YyyER8,20842
66
+ realign/dashboard/screens/event_detail.py,sha256=-pqt3NBoeTXGJKtbndZy-msklwXTeNWMS4H12oMG5ks,20175
62
67
  realign/dashboard/screens/help_screen.py,sha256=Icrcvbgyz49R2tBiu8vBZ4CLm6iYclv_-FTa2pCFRRQ,3398
63
68
  realign/dashboard/screens/session_detail.py,sha256=TBkHqSHyMxsLB2QdZq9m1EoiH8oRVDbPrjt-a8I9sHs,9561
64
69
  realign/dashboard/screens/share_import.py,sha256=hl2x0yGVycsoUI76AmdZTAV-br3Q6191g5xHHrZ8hOA,6318
65
- realign/dashboard/styles/dashboard.tcss,sha256=ewonevBGLN-dfSsgxUk4VBCPchtxY4rx_vj1u6Ox2Fw,3454
66
- realign/dashboard/widgets/__init__.py,sha256=3Pf2_K9obrertgv_psfxradgkI9RXlmjoXYQH7oBKm0,583
70
+ realign/dashboard/styles/dashboard.tcss,sha256=AhYhvm1hBZee0Gzv0C8qsx_-6DqLw4Gg-h_XiOGQHJ0,4557
71
+ realign/dashboard/widgets/__init__.py,sha256=1FBQzar5Jd4hBZtANVKb_dNxFAqVqrwJJkBpeLmCq-Q,640
72
+ realign/dashboard/widgets/agents_panel.py,sha256=GxnOrg6C1dPw0lhOfrjIHJAk04MMwhYJwMUY1SfMuCs,38875
67
73
  realign/dashboard/widgets/config_panel.py,sha256=eRJRuqImQ8eJIKCEj4O8EvYxI-ht_anrcYbT5JskWyU,15972
68
- realign/dashboard/widgets/events_table.py,sha256=MKB1G1_xdQCujEhmMz_GKI4hs-PeEiqGEAH7Y3ZGanE,30852
74
+ realign/dashboard/widgets/events_table.py,sha256=0cMvE0KdZFBZyvywv7vlt005qsR0aLQnQiMf3ZzK7RY,30218
69
75
  realign/dashboard/widgets/header.py,sha256=0HHCFXX7F3C6HII-WDwOJwWkJrajmKPWmdoMWyOkn9E,1587
70
76
  realign/dashboard/widgets/openable_table.py,sha256=GeJPDEYp0kRHShqvmPMzAePpYXRZHUNqcWNnxqsqxjA,1963
71
77
  realign/dashboard/widgets/search_panel.py,sha256=ZNJDfwDSxUFnCeltYQYsQsPJ6t4HDeNWpENoTOoBdVM,8951
72
- realign/dashboard/widgets/sessions_table.py,sha256=oMkYhQ55pUGOGYxEXM5P37mpGYA350BK8Rb8fVq9AS4,34008
73
- realign/dashboard/widgets/terminal_panel.py,sha256=8WX2_EewlyFlxJYokw2akEqkJUjNt_-F8tzE7St3084,60132
78
+ realign/dashboard/widgets/sessions_table.py,sha256=6y78pEkyAmNsU4_o46PbwXRFW17fc5khgheBi4LjBNg,33374
79
+ realign/dashboard/widgets/terminal_panel.py,sha256=at8whXa8Bsn_icbyerHG21tb2BsnQikAMlf4NfIpTGw,61504
74
80
  realign/dashboard/widgets/watcher_panel.py,sha256=emVY1-aot9Dnf5UI9yyNeEmp4d2Gb-lrC28DjkeLjKA,19575
75
81
  realign/dashboard/widgets/worker_panel.py,sha256=F_jKWABuCNmjQgeeuCr4KnFRKdY4CLTNcEXMYwsNaSk,18691
76
82
  realign/db/__init__.py,sha256=65LsNdsq_rkwNC1eg1OAr3HC0ORXtelOh0I8MhNGr-g,3288
77
- realign/db/base.py,sha256=5baEwoGR5X2SyQXkXuyeUP2zelcuQardVouD2S-qils,13703
78
- realign/db/locks.py,sha256=yzCiPJZ4eOQX-Q4mXB6s76U2U7lXAzIBBy1t59w-AVU,1698
83
+ realign/db/base.py,sha256=XIW25zh8UD2AC4zJaGXWmRkcZxk59nVwqDFysk_vfZw,14370
84
+ realign/db/locks.py,sha256=dUQu9Yo5nZstMSPXZPYzN0xqX8UXhJgNV_PmYEJ-rK0,1801
79
85
  realign/db/migrate_agents.py,sha256=cDeVUzKW950dJ0lV74QObHuONqKwErSrXI5akU2vBmQ,9633
80
86
  realign/db/migration.py,sha256=af1QFEfIh_qX0pFyXzm5gWFVbQn0sKOUNLSJHlr__FU,13405
81
- realign/db/schema.py,sha256=YHj5PGZWbCl0VG0epnMF_Ofg3jRiLHq6SLHCi1q34eQ,30181
82
- realign/db/sqlite_db.py,sha256=nihEZ71wg1BXiVG1QU488ed9Q-ZasoVKYVS4j20hhtY,107223
87
+ realign/db/schema.py,sha256=uOp8B7-zq5nsdyw5aNpwhhAr5lnoYAWNES6Mr1GCh7Y,31803
88
+ realign/db/sqlite_db.py,sha256=Ixcym-aUtVkgpav0FQ3whCp6PblwBtMGrHmvK8GHvMY,113707
83
89
  realign/events/__init__.py,sha256=IM-NxF4Zk2hYFD07k4WrfNRuuiC9ihGjf4GBpJhjd2E,35
90
+ realign/events/agent_summarizer.py,sha256=vh65tYgo1NOYsIpVPR253nnOr-MIejC4KG5dGvDzKv4,5413
84
91
  realign/events/debouncer.py,sha256=U3Q7dYpnMsAgWsW_E_IbSC4lrdEoi6H_SFLGLOAazs4,3062
85
92
  realign/events/event_summarizer.py,sha256=jJtWM8UWtsG4KGdzYicMqcTxrncWzGNEQs5vdBJPyew,10185
86
- realign/events/session_summarizer.py,sha256=EzFFjgcUEAqm1IJuxOAPx74GTUIStyXTvQ9maZ-V10U,12888
93
+ realign/events/session_summarizer.py,sha256=LqBoICEF5cggdv3px2O2TP0Xk7h_LofE1DDRv2TyJ5I,13868
87
94
  realign/models/event.py,sha256=ypz74D4l6U2U0RhgL8fzEhiq7iQjhHybmAdLUNDY7P4,5521
88
95
  realign/prompts/__init__.py,sha256=PpYR7f-T96fd-QyNYJDRS1U6h9O0rIt_SMsREy9i3aA,443
89
96
  realign/prompts/presets.py,sha256=h9oEy0XP4JQ4DCnp8HN_FfF0LmI-yOV6xWJLknIghJ8,7256
@@ -97,8 +104,8 @@ realign/triggers/next_turn_trigger.py,sha256=-x80_I-WmIjXXzQHEPBykgx_GQW6oKaLDQx
97
104
  realign/triggers/registry.py,sha256=dkIjSd8Bg-hF0nxaO2Fi2K-0Zipqv6vVjc-HYSrA_fY,3656
98
105
  realign/triggers/turn_status.py,sha256=wAZEhXDAmDoX5F-ohWfSnZZ0eA6DAJ9svSPiSv_f6sg,6041
99
106
  realign/triggers/turn_summary.py,sha256=f3hEUshgv9skJ9AbfWpoYs417lsv_HK2A_vpPjgryO4,4467
100
- aline_ai-0.6.5.dist-info/METADATA,sha256=RmD0VjSn_0nGStyFKgkNYUL3i2foaqg8UWUT3kOUTOc,1597
101
- aline_ai-0.6.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
102
- aline_ai-0.6.5.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
103
- aline_ai-0.6.5.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
104
- aline_ai-0.6.5.dist-info/RECORD,,
107
+ aline_ai-0.6.6.dist-info/METADATA,sha256=_RwRrqtSa3O_Rt0sXdK18VX8fHbYtz9gtE-tkVfGe5w,1597
108
+ aline_ai-0.6.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
109
+ aline_ai-0.6.6.dist-info/entry_points.txt,sha256=TvYELpMoWsUTcQdMV8tBHxCbEf_LbK4sESqK3r8PM6Y,78
110
+ aline_ai-0.6.6.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
111
+ aline_ai-0.6.6.dist-info/RECORD,,
realign/__init__.py CHANGED
@@ -3,7 +3,7 @@
3
3
  import hashlib
4
4
  from pathlib import Path
5
5
 
6
- __version__ = "0.6.5"
6
+ __version__ = "0.6.6"
7
7
 
8
8
 
9
9
  def get_realign_dir(project_root: Path) -> Path:
realign/agent_names.py ADDED
@@ -0,0 +1,79 @@
1
+ """Random Docker-style agent name generator.
2
+
3
+ Generates names like "bold-turing", "swift-hopper", etc.
4
+ """
5
+
6
+ import random
7
+
8
+ ADJECTIVES = [
9
+ "bold",
10
+ "bright",
11
+ "calm",
12
+ "clever",
13
+ "crisp",
14
+ "deft",
15
+ "eager",
16
+ "fast",
17
+ "firm",
18
+ "glad",
19
+ "keen",
20
+ "lucid",
21
+ "neat",
22
+ "noble",
23
+ "prime",
24
+ "quick",
25
+ "sharp",
26
+ "sleek",
27
+ "smart",
28
+ "solid",
29
+ "steady",
30
+ "strong",
31
+ "subtle",
32
+ "sure",
33
+ "swift",
34
+ "tidy",
35
+ "vivid",
36
+ "warm",
37
+ "wise",
38
+ "witty",
39
+ ]
40
+
41
+ SURNAMES = [
42
+ "babbage",
43
+ "bell",
44
+ "boole",
45
+ "cerf",
46
+ "church",
47
+ "conway",
48
+ "curie",
49
+ "darwin",
50
+ "dijkstra",
51
+ "euler",
52
+ "faraday",
53
+ "feynman",
54
+ "gauss",
55
+ "hopper",
56
+ "johnson",
57
+ "kahn",
58
+ "knuth",
59
+ "lamarr",
60
+ "leibniz",
61
+ "lovelace",
62
+ "maxwell",
63
+ "neumann",
64
+ "noether",
65
+ "pascal",
66
+ "planck",
67
+ "ritchie",
68
+ "shannon",
69
+ "tesla",
70
+ "thompson",
71
+ "turing",
72
+ ]
73
+
74
+ _rng = random.SystemRandom()
75
+
76
+
77
+ def generate_agent_name() -> str:
78
+ """Return a random Docker-style name, e.g. 'bold-turing'."""
79
+ return f"{_rng.choice(ADJECTIVES)}-{_rng.choice(SURNAMES)}"
@@ -66,6 +66,7 @@ def main():
66
66
  terminal_id = os.environ.get("ALINE_TERMINAL_ID", "")
67
67
  inner_socket = os.environ.get("ALINE_INNER_TMUX_SOCKET", "")
68
68
  inner_session = os.environ.get("ALINE_INNER_TMUX_SESSION", "")
69
+ agent_id = os.environ.get("ALINE_AGENT_ID", "")
69
70
 
70
71
  if not terminal_id:
71
72
  try:
@@ -104,6 +105,7 @@ def main():
104
105
  signal_data = {
105
106
  "session_id": session_id,
106
107
  "terminal_id": terminal_id,
108
+ "agent_id": agent_id,
107
109
  "project_dir": project_dir,
108
110
  "transcript_path": transcript_path,
109
111
  "cwd": cwd,
@@ -348,6 +350,7 @@ def main():
348
350
  cwd=cwd,
349
351
  project_dir=project_dir,
350
352
  source="Stop",
353
+ agent_id=agent_id if agent_id else None,
351
354
  )
352
355
  except Exception:
353
356
  pass
@@ -56,6 +56,7 @@ def _write_to_db(
56
56
  source: str = "",
57
57
  context_id: Optional[str] = None,
58
58
  attention: Optional[str] = None,
59
+ agent_id: Optional[str] = None,
59
60
  ) -> bool:
60
61
  """Write terminal mapping to database (best-effort).
61
62
 
@@ -96,6 +97,14 @@ def _write_to_db(
96
97
  source=source if source else None,
97
98
  attention=attention,
98
99
  )
100
+
101
+ # Link session to agent if both are available (V19+)
102
+ if session_id and agent_id:
103
+ try:
104
+ db.update_session_agent_id(session_id, agent_id)
105
+ except Exception:
106
+ pass
107
+
99
108
  # Note: Don't close - get_database() returns a singleton
100
109
  return True
101
110
  except Exception:
@@ -114,6 +123,7 @@ def update_terminal_mapping(
114
123
  source: str = "",
115
124
  context_id: Optional[str] = None,
116
125
  attention: Optional[str] = None,
126
+ agent_id: Optional[str] = None,
117
127
  ) -> None:
118
128
  """Update terminal->session binding.
119
129
 
@@ -135,6 +145,7 @@ def update_terminal_mapping(
135
145
  source=source,
136
146
  context_id=context_id,
137
147
  attention=attention,
148
+ agent_id=agent_id,
138
149
  )
139
150
 
140
151
  # Phase 2: Write to JSON (backward compatibility)
@@ -52,6 +52,7 @@ def main() -> None:
52
52
  terminal_id = os.environ.get("ALINE_TERMINAL_ID", "")
53
53
  inner_socket = os.environ.get("ALINE_INNER_TMUX_SOCKET", "")
54
54
  inner_session = os.environ.get("ALINE_INNER_TMUX_SESSION", "")
55
+ agent_id = os.environ.get("ALINE_AGENT_ID", "")
55
56
 
56
57
  if not terminal_id:
57
58
  try:
@@ -86,6 +87,7 @@ def main() -> None:
86
87
  signal_data = {
87
88
  "session_id": session_id,
88
89
  "terminal_id": terminal_id,
90
+ "agent_id": agent_id,
89
91
  "prompt": prompt,
90
92
  "transcript_path": transcript_path,
91
93
  "cwd": cwd,
@@ -274,6 +276,7 @@ def main() -> None:
274
276
  cwd=cwd,
275
277
  project_dir=project_dir,
276
278
  source="UserPromptSubmit",
279
+ agent_id=agent_id if agent_id else None,
277
280
  )
278
281
  except Exception:
279
282
  pass
realign/cli.py CHANGED
@@ -19,6 +19,7 @@ from .commands import (
19
19
  add,
20
20
  auth,
21
21
  doctor,
22
+ agent,
22
23
  )
23
24
 
24
25
  app = typer.Typer(
@@ -248,6 +249,67 @@ app.add_typer(context_app, name="context")
248
249
  add_app = typer.Typer(help="Install optional local tooling")
249
250
  app.add_typer(add_app, name="add")
250
251
 
252
+ # Create agent subcommand group
253
+ agent_app = typer.Typer(help="Manage agents")
254
+ app.add_typer(agent_app, name="agent")
255
+
256
+
257
+ @agent_app.command(name="new")
258
+ def agent_new_cli(
259
+ name: Optional[str] = typer.Option(None, "--name", "-n", help="Agent name"),
260
+ desc: str = typer.Option("", "--desc", "-d", help="Agent description"),
261
+ ):
262
+ """Create a new agent with a random name (or specify one)."""
263
+ exit_code = agent.agent_new_command(name=name, desc=desc)
264
+ raise typer.Exit(code=exit_code)
265
+
266
+
267
+ @agent_app.command(name="list")
268
+ def agent_list_cli(
269
+ all: bool = typer.Option(
270
+ False, "--all", "-a", help="Include invisible agents"
271
+ ),
272
+ ):
273
+ """List all agents."""
274
+ exit_code = agent.agent_list_command(include_invisible=all)
275
+ raise typer.Exit(code=exit_code)
276
+
277
+
278
+ @agent_app.command(name="share")
279
+ def agent_share_cli(
280
+ agent_id: str = typer.Argument(..., help="Agent ID to share"),
281
+ password: Optional[str] = typer.Option(
282
+ None, "--password", "-p", help="Password for encrypted share"
283
+ ),
284
+ expiry_days: int = typer.Option(7, "--expiry", help="Number of days before share expires"),
285
+ max_views: int = typer.Option(100, "--max-views", help="Maximum number of views allowed"),
286
+ mcp: bool = typer.Option(
287
+ True,
288
+ "--mcp/--no-mcp",
289
+ help="Include MCP usage instructions (default: enabled)",
290
+ ),
291
+ json_output: bool = typer.Option(False, "--json", help="Output results in JSON format"),
292
+ ):
293
+ """Share all sessions for an agent.
294
+
295
+ Creates a shareable link for all sessions associated with the specified agent.
296
+ The share includes a generated Slack message for easy sharing.
297
+
298
+ Examples:
299
+ aline agent share abc123de
300
+ aline agent share abc123de --json
301
+ aline agent share abc123de --password mypass
302
+ """
303
+ exit_code = export_shares.export_agent_shares_command(
304
+ agent_id=agent_id,
305
+ password=password,
306
+ expiry_days=expiry_days,
307
+ max_views=max_views,
308
+ enable_mcp=mcp,
309
+ json_output=json_output,
310
+ )
311
+ raise typer.Exit(code=exit_code)
312
+
251
313
 
252
314
  @add_app.command(name="tmux")
253
315
  def add_tmux_cli():
realign/codex_detector.py CHANGED
@@ -14,7 +14,7 @@ def _codex_session_roots() -> list[Path]:
14
14
  # Default Codex home: ~/.codex/sessions
15
15
  roots.append(Path.home() / ".codex" / "sessions")
16
16
 
17
- # Aline-managed per-terminal CODEX_HOME isolation: ~/.aline/codex_homes/*/sessions
17
+ # Aline-managed per-terminal or per-agent CODEX_HOME isolation: ~/.aline/codex_homes/*/sessions
18
18
  try:
19
19
  from .codex_home import aline_codex_homes_dir, codex_sessions_dir_for_home
20
20
 
realign/codex_home.py CHANGED
@@ -1,10 +1,8 @@
1
1
  """Codex home/session path helpers.
2
2
 
3
- To guarantee terminal↔session binding even when multiple Codex instances run in the same cwd,
4
- we can isolate Codex storage per dashboard terminal via the `CODEX_HOME` environment variable.
5
-
6
- We choose deterministic paths under `~/.aline/` so the watcher (a separate process) can
7
- derive the owning terminal_id purely from the session file path.
3
+ We isolate Codex storage via `CODEX_HOME` so the watcher can infer ownership
4
+ from the session file path. By default it's per-terminal, but when an
5
+ ALINE_AGENT_ID is present we can scope CODEX_HOME per agent.
8
6
  """
9
7
 
10
8
  from __future__ import annotations
@@ -15,6 +13,7 @@ from typing import Optional
15
13
 
16
14
 
17
15
  ENV_CODEX_HOME = "CODEX_HOME"
16
+ AGENT_HOME_PREFIX = "agent-"
18
17
 
19
18
 
20
19
  def aline_codex_homes_dir() -> Path:
@@ -24,11 +23,26 @@ def aline_codex_homes_dir() -> Path:
24
23
  return Path.home() / ".aline" / "codex_homes"
25
24
 
26
25
 
26
+ def _safe_id(raw_id: str) -> str:
27
+ return (raw_id or "").strip().replace("/", "_").replace("\\", "_")
28
+
29
+
27
30
  def codex_home_for_terminal(terminal_id: str) -> Path:
28
- tid = (terminal_id or "").strip()
31
+ tid = _safe_id(terminal_id)
29
32
  return aline_codex_homes_dir() / tid
30
33
 
31
34
 
35
+ def codex_home_for_agent(agent_id: str) -> Path:
36
+ aid = _safe_id(agent_id)
37
+ return aline_codex_homes_dir() / f"{AGENT_HOME_PREFIX}{aid}"
38
+
39
+
40
+ def codex_home_for_terminal_or_agent(terminal_id: str, agent_id: Optional[str]) -> Path:
41
+ if agent_id:
42
+ return codex_home_for_agent(agent_id)
43
+ return codex_home_for_terminal(terminal_id)
44
+
45
+
32
46
  def codex_sessions_dir_for_home(codex_home: Path) -> Path:
33
47
  return codex_home / "sessions"
34
48
 
@@ -37,8 +51,14 @@ def codex_sessions_dir_for_terminal(terminal_id: str) -> Path:
37
51
  return codex_sessions_dir_for_home(codex_home_for_terminal(terminal_id))
38
52
 
39
53
 
40
- def terminal_id_from_codex_session_file(session_file: Path) -> Optional[str]:
41
- """If session_file is under an Aline-managed CODEX_HOME, return terminal_id."""
54
+ def codex_sessions_dir_for_terminal_or_agent(
55
+ terminal_id: str, agent_id: Optional[str]
56
+ ) -> Path:
57
+ return codex_sessions_dir_for_home(codex_home_for_terminal_or_agent(terminal_id, agent_id))
58
+
59
+
60
+ def codex_home_owner_from_session_file(session_file: Path) -> Optional[tuple[str, str]]:
61
+ """Return ("terminal", id) or ("agent", id) if session_file is under Aline-managed homes."""
42
62
  try:
43
63
  homes = aline_codex_homes_dir().resolve()
44
64
  p = session_file.resolve()
@@ -53,17 +73,29 @@ def terminal_id_from_codex_session_file(session_file: Path) -> Optional[str]:
53
73
  parts = rel.parts
54
74
  if len(parts) < 3:
55
75
  return None
56
- terminal_id = (parts[0] or "").strip()
57
- if not terminal_id:
76
+ owner = (parts[0] or "").strip()
77
+ if not owner:
58
78
  return None
59
79
  if parts[1] != "sessions":
60
80
  return None
61
- return terminal_id
81
+ if owner.startswith(AGENT_HOME_PREFIX):
82
+ return ("agent", owner[len(AGENT_HOME_PREFIX) :])
83
+ return ("terminal", owner)
62
84
 
63
85
 
64
- def prepare_codex_home(terminal_id: str) -> Path:
65
- """Create/prepare an isolated CODEX_HOME for a terminal (best-effort)."""
66
- home = codex_home_for_terminal(terminal_id)
86
+ def terminal_id_from_codex_session_file(session_file: Path) -> Optional[str]:
87
+ """If session_file is under an Aline-managed CODEX_HOME, return terminal_id."""
88
+ owner = codex_home_owner_from_session_file(session_file)
89
+ if not owner:
90
+ return None
91
+ if owner[0] != "terminal":
92
+ return None
93
+ return owner[1]
94
+
95
+
96
+ def prepare_codex_home(terminal_id: str, *, agent_id: Optional[str] = None) -> Path:
97
+ """Create/prepare an isolated CODEX_HOME (per-agent if agent_id is provided)."""
98
+ home = codex_home_for_terminal_or_agent(terminal_id, agent_id)
67
99
  sessions = codex_sessions_dir_for_home(home)
68
100
  try:
69
101
  sessions.mkdir(parents=True, exist_ok=True)
@@ -82,4 +114,3 @@ def prepare_codex_home(terminal_id: str) -> Path:
82
114
  pass
83
115
 
84
116
  return home
85
-
@@ -13,6 +13,7 @@ from __future__ import annotations
13
13
  import json
14
14
  from dataclasses import dataclass
15
15
  from datetime import datetime, timezone
16
+ import os
16
17
  from pathlib import Path
17
18
  from typing import Iterable, Optional, Protocol
18
19
 
@@ -42,6 +43,16 @@ def _parse_iso8601(ts: str) -> Optional[datetime]:
42
43
  return dt
43
44
 
44
45
 
46
+ def _normalize_cwd(cwd: str | None) -> str:
47
+ raw = (cwd or "").strip()
48
+ if not raw:
49
+ return ""
50
+ try:
51
+ return os.path.normpath(raw)
52
+ except Exception:
53
+ return raw.rstrip("/\\")
54
+
55
+
45
56
  def read_codex_session_meta(session_file: Path) -> Optional[CodexSessionMeta]:
46
57
  """Extract Codex session metadata from a session file (best-effort)."""
47
58
  try:
@@ -108,10 +119,10 @@ def select_agent_for_codex_session(
108
119
  agents: Iterable[_AgentLike],
109
120
  *,
110
121
  session: CodexSessionMeta,
111
- max_time_delta_seconds: int = 6 * 60 * 60,
122
+ max_time_delta_seconds: Optional[int] = 6 * 60 * 60,
112
123
  ) -> Optional[str]:
113
124
  """Pick the best active Codex terminal for a Codex session file (best-effort)."""
114
- cwd = (session.cwd or "").strip()
125
+ cwd = _normalize_cwd(session.cwd)
115
126
  if not cwd:
116
127
  return None
117
128
 
@@ -122,7 +133,7 @@ def select_agent_for_codex_session(
122
133
  continue
123
134
  if getattr(a, "provider", "") != "codex":
124
135
  continue
125
- if (getattr(a, "cwd", None) or "").strip() != cwd:
136
+ if _normalize_cwd(getattr(a, "cwd", None)) != cwd:
126
137
  continue
127
138
  # Avoid clobbering an existing binding to a different session.
128
139
  existing_sid = (getattr(a, "session_id", None) or "").strip()
@@ -165,8 +176,8 @@ def select_agent_for_codex_session(
165
176
 
166
177
  if best_id is None:
167
178
  return None
168
- if best_delta is not None and best_delta > max_time_delta_seconds:
169
- # Ambiguous: don't bind if terminals are too far from the session start.
170
- return None
179
+ if max_time_delta_seconds is not None and best_delta is not None:
180
+ if best_delta > max_time_delta_seconds:
181
+ # Ambiguous: don't bind if terminals are too far from the session start.
182
+ return None
171
183
  return best_id
172
-