کدکس از نسخه‌ی 0.136.0 سه فرمان archive، unarchive و delete را برای نشست‌های ذخیره‌شده دارد، و از نسخه‌ی 0.140.0 سومی کامل شده است. archive فایل نشست را از ~/.codex/sessions به ~/.codex/archived_sessions جابه‌جا می‌کند و متن آن را دست‌نخورده نگه می‌دارد؛ md5sum قبل و بعد از فرمان یکسان است. در این پست هر سه فرمان را روی یک CODEX_HOME تازه اجرا می‌کنیم و سه خطای واقعی را می‌بینیم: codex archive روی نشستی که از پیش آرشیو شده خطا می‌دهد، نام نشست به‌جای UUID کار نمی‌کند، و delete بدون ترمینال تعاملی تایید نمی‌شود.

سه فرمان، یک قرارداد

کدکس نشست‌های تعاملی را به‌صورت یک فایل .jsonl در پوشه‌ی ~/.codex/sessions نگه می‌دارد، درختی که با تاریخ دسته‌بندی شده است: مسیر هر فایل سال، ماه و روز را در خود دارد. جدول زیر سه فرمان را کنار هم می‌گذارد؛ ستون آخر چیزی است که هر فرمان روی دیسک عوض می‌کند.

فرمانکاراثر روی دیسکمتن نشست
codex archiveپنهان‌کردن از فهرست فعالجابه‌جایی به archived_sessionsمی‌ماند
codex unarchiveبرگرداندن به فهرست فعالبازگشت به sessionsمی‌ماند
codex deleteپاک‌کردن برای همیشهحذف فایلنابود می‌شود

این سه فرمان در مرجع فرمان‌های توسعه‌دهنده‌ی کدکس با برچسب پایدار آمده‌اند و همان‌جا نوشته شده که از آن‌ها برای مرتب‌کردن فهرست نشست‌ها بدون حذف رونوشت استفاده کنید. در جدول فرمان‌های خط فرمان هم هر سه در یک ردیف کنار هم آمده‌اند، درست کنار codex resume و codex fork.

این فرمان‌ها از کدام نسخه آمدند

عدد دقیق را از کد خود کدکس درمی‌آوریم، نه از یادداشت انتشار. تعریف هر سه فرمان در فایل codex-rs/cli/src/main.rs است و من هر برچسب انتشار را در همان فایل جست‌وجو کردم. نتیجه سه نقطه‌ی روشن دارد، و تاریخ هر نقطه از صفحه‌ی انتشار همان برچسب در گیت‌هاب خوانده شده است.

برچسبتاریخ انتشارarchiveunarchivedelete
rust-v0.134.0۲۶ مه ۲۰۲۶نهنهنه
rust-v0.136.0۱ ژوئن ۲۰۲۶بلهبلهنه
rust-v0.140.0۱۵ ژوئن ۲۰۲۶بلهبلهبله

پس تاریخ دقیق archive و unarchive یک ژوئن ۲۰۲۶ است و تاریخ delete پانزده ژوئن ۲۰۲۶. یادداشت انتشار نسخه‌ی 0.155.0 در ۱۷ سپتامبر ۲۰۲۶ از افزوده‌شدن کنش‌های آرشیو و حذف به نمای کلی نشست‌ها خبر می‌دهد، یعنی همان قابلیت‌ها سه ماه بعد به رابط کاربری هم رسیدند.

نسخه‌ی تازه‌ای که همین لحظه روی npm است 0.160.1 است و در ۵ اکتبر ۲۰۲۶ منتشر شد؛ آن نسخه یک اصلاح درباره‌ی متغیرهای محیطی دارد و ربطی به این سه فرمان ندارد. نسخه‌ای که روی همین سرور نصب است 0.158.0 است که در ۲۸ سپتامبر ۲۰۲۶ بیرون آمد و هر سه فرمان را دارد.

ساخت یک نشست و آرشیو کردن آن

برای اینکه آزمایش به نشست‌های واقعی این سرور دست نزند، یک CODEX_HOME تازه می‌سازیم و دو فایل نشست داخلش می‌گذاریم. لازم نیست کدکس را نصب کنیم؛ نسخه‌ی نصب‌شده هر سه فرمان را دارد و یک فایل نشست هم فقط دو خط JSON است. مسیر را بیرون از /tmp می‌بریم، چون کدکس در پوشه‌های موقت از ساختن فایل‌های کمکی طفره می‌رود و یک هشدار اضافه چاپ می‌کند.

<span class="code-comment"># یک خانه‌ی تازه برای کدکس، تا نشست‌های واقعی این سرور دست‌نخورده بماند</span>
$ <span class="code-builtin">export</span> CODEX_HOME=/root/.cache/cxdemo/home
$ <span class="code-builtin">mkdir</span> -p <span class="code-str">"$CODEX_HOME"</span>/sessions/2026/09/03 <span class="code-str">"$CODEX_HOME"</span>/sessions/2026/10/06

<span class="code-comment"># برای هر نشست دو خط بنویس: session_meta و turn_context</span>
$ <span class="code-builtin">python3</span> - <span class="code-str">"$CODEX_HOME"</span> <<<span class="code-str">'PY'</span>
<span class="code-keyword">import</span> json, os, pathlib
home = pathlib.Path(os.environ[<span class="code-str">"CODEX_HOME"</span>])
jobs = {<span class="code-str">"old"</span>: (<span class="code-str">"2026/09/03"</span>, <span class="code-str">"2026-09-03T09:00:00.000Z"</span>),
        <span class="code-str">"new"</span>: (<span class="code-str">"2026/10/06"</span>, <span class="code-str">"2026-10-06T09:00:00.000Z"</span>)}
<span class="code-keyword">for</span> tag, (day, ts) <span class="code-keyword">in</span> jobs.items():
    p = home / <span class="code-str">"sessions"</span> / day
    p.mkdir(parents=<span class="code-keyword">True</span>, exist_ok=<span class="code-keyword">True</span>)
    rows = [{<span class="code-str">"type"</span>: <span class="code-str">"session_meta"</span>,
             <span class="code-str">"payload"</span>: {<span class="code-str">"id"</span>: tag, <span class="code-str">"cwd"</span>: <span class="code-str">"/tmp"</span>, <span class="code-str">"timestamp"</span>: ts}},
            {<span class="code-str">"type"</span>: <span class="code-str">"turn_context"</span>, <span class="code-str">"payload"</span>: {<span class="code-str">"cwd"</span>: <span class="code-str">"/tmp"</span>}}]
    (p / f<span class="code-str">"rollout-{ts}-{tag}.jsonl"</span>).write_text(
        <span class="code-str">""</span>.join(json.dumps(r) + <span class="code-str">"\n"</span> <span class="code-keyword">for</span> r <span class="code-keyword">in</span> rows))
PY
$ <span class="code-builtin">find</span> <span class="code-str">"$CODEX_HOME"</span>/sessions -name <span class="code-str">'*.jsonl'</span> | <span class="code-builtin">sort</span>
/root/.cache/cxdemo/home/sessions/2026/09/03/rollout-2026-09-03T09:00:00.000Z-old.jsonl
/root/.cache/cxdemo/home/sessions/2026/10/06/rollout-2026-10-06T09:00:00.000Z-new.jsonl

حالا آرشیو را با چکش رمز اجرا می‌کنیم. عدد کلید قبل و بعد از فرمان یکسان است و همین یک عدد ثابت می‌کند که فرمان متن را بازنویسی نکرده، فقط فایل را جابه‌جا کرده است.

$ <span class="code-builtin">md5sum</span> <span class="code-str">"$CODEX_HOME"</span>/sessions/2026/09/03/*.jsonl
bbaf1362b3aceff0efcc71e296001450  .../2026/09/03/rollout-2026-09-03T09:00:00.000Z-old.jsonl

<span class="code-comment"># آرشیو با UUID؛ نام نشست در این مرحله هنوز شناخته نمی‌شود</span>
$ <span class="code-builtin">codex</span> archive 00000000-0000-0000-0000-0000000000b1
Archived session 00000000-0000-0000-0000-0000000000b1.
$ <span class="code-builtin">echo</span> <span class="code-str">$?</span>
0

$ <span class="code-builtin">md5sum</span> <span class="code-str">"$CODEX_HOME"</span>/archived_sessions/*.jsonl
bbaf1362b3aceff0efcc71e296001450  .../archived_sessions/rollout-2026-09-03T09:00:00.000Z-old.jsonl

نکته‌ی ریزی که بعداً دردسر می‌دهد همین‌جاست: unarchive نشست را به پوشه‌ی تاریخی خودش برمی‌گرداند، نه به پوشه‌ی امروز. نشستی که با تاریخ سوم سپتامبر ذخیره شده بود دوباره به 2026/09/03 برگشت، با آنکه در شش ژوئن از آرشیو بیرون آمد. اگر نشستی دارید که ماه‌ها آرشیو مانده بوده، همان مسیر قدیمی را برمی‌گرداند.

سه خطای واقعی که باید بدانید

این بخش دلیل نوشته‌شدن پست است. هر سه خطا را در همان خانه‌ی آزمایشی گرفتیم و هر سه کد خروج ۱ دارند، یعنی در خط لوله‌ی خودکار یک توقف واقعی‌اند و نه یک هشدار گذرا.

<span class="code-comment"># ۱) نشستی که از پیش آرشیو شده: خطای عمومی، بدون نام</span>
$ <span class="code-builtin">codex</span> archive 00000000-0000-0000-0000-0000000000b1
Error: failed to archive session
$ <span class="code-builtin">echo</span> <span class="code-str">$?</span>
1

<span class="code-comment"># ۲) نامی که هیچ نشستی ندارد: پیام کاملا متفاوت</span>
$ <span class="code-builtin">codex</span> archive nightly-demo
Error: No active session found matching 'nightly-demo'.
$ <span class="code-builtin">echo</span> <span class="code-str">$?</span>
1

<span class="code-comment"># ۳) UUID ناموجود: دوباره همان خطای بی‌نام</span>
$ <span class="code-builtin">codex</span> archive 99999999-9999-9999-9999-999999999999
Error: failed to archive session
$ <span class="code-builtin">echo</span> <span class="code-str">$?</span>
1

پس پیام failed to archive session دو علت کاملا متفاوت را پنهان می‌کند: نشست از قبل آرشیو شده، و نشست اصلاً وجود ندارد. تنها راه تشخیص، نگاه کردن به پوشه است؛ اگر فایل در archived_sessions بود، خودتان از پیش آرشیو کرده‌اید. این خطا برای یک فرمان در خط لوله بی‌فایده است، چون کدکس نمی‌گوید کدام‌یک از دو شد.

خطای دوم یعنی archive نام نشست را از خود فایل .jsonl نمی‌خواند. وقتی سطر session_meta را دستی نوشتیم، هیچ فیلدی به نام نشست در آن نبود و فرمان نام را نشناخت. ستون name در جدول threads از پایگاه‌داده‌ی state_5.sqlite می‌آید، نه از فایل نشست. این را با آزمون اثبات کردیم: همان فایل را با همان UUID نگه داشتیم، مقدار ستون name را در پایگاه‌داده نوشتیم، و فرمان باز هم نام را پیدا نکرد. نتیجه‌ی عملی این است که در خط لوله UUID را بدهید؛ نام فقط روی نشستی قابل اتکاست که خود کدکس ساخته و در فهرست نام‌گذاری کرده است.

حذف، تنها فرمانی که تایید می‌خواهد

archive بی‌خطر است چون متن را نگه می‌دارد، و delete تنها فرمانی است که بدون تایید کار نمی‌کند. در خط فرمان غیرتعاملی هر دو حالت شکست می‌خورند و هر پیام دقیقاً می‌گوید چه کنید.

<span class="code-comment"># ۱) بدون ترمینال تعاملی: تایید ممکن نیست</span>
$ <span class="code-builtin">codex</span> delete 00000000-0000-0000-0000-0000000000b1
Error: cannot confirm session deletion without an interactive terminal; rerun with --force and a session UUID
$ <span class="code-builtin">echo</span> <span class="code-str">$?</span>
1

<span class="code-comment"># ۲) --force با نام، حتی با ترمینال تعاملی، رد می‌شود</span>
$ <span class="code-builtin">codex</span> delete --force nightly-demo
Error: --force requires a session UUID; names must be confirmed interactively
$ <span class="code-builtin">echo</span> <span class="code-str">$?</span>
1

--force فقط با UUID کار می‌کند و دلیلش در خود کد کدکس نوشته شده است: نام‌ها باید تعاملی تایید شوند تا یک نام تکراری یا مبهم بی‌سؤال پاک نشود. برای همین حذف در خط لوله همیشه دو مرحله دارد، اول آرشیو و بعد delete --force با UUID. اگر مرحله‌ی اول شکست بخورد و پیام failed to archive session بگیرید، delete --force را اجرا نکنید، چون آن‌وقت نشست در جایی هست که انتظارش را ندارید.

فرماننیاز به ترمینالورودی مجازکد خروج موفق
archiveنهUUID یا نام ثبت‌شده۰
unarchiveنهUUID یا نام ثبت‌شده۰
deleteبلهUUID یا نام با تایید۰
delete --forceنهفقط UUID۰

چه چیزی را عوض می‌کنند و چه چیزی را نه

این سه فرمان یک چیز را اضافه می‌کنند که پیش از این نبود: پاک‌کردن فهرست نشست‌ها بدون دور انداختن متن. اگر بعد از هر کار یک نشست تازه باز می‌کنید و فهرست شما بعد از چند هفته به یک صفحه‌ی طولانی تبدیل می‌شود، آرشیو همان چیزی است که لازم دارید؛ متن نشست سر جایش می‌ماند و هر وقت لازم شد با unarchive برمی‌گردد.

دو چیز را این فرمان‌ها انجام نمی‌دهند. اول، فضای دیسک را آزاد نمی‌کنند، چون فایل جابه‌جا می‌شود و نه پاک؛ اگر هدف شما کم‌کردن حجم است، delete --force لازم است. دوم، روی نشست‌هایی که خودتان فایلشان را دستی ساخته‌اید نام‌شان را پیدا نمی‌کنند، چون نام از پایگاه‌داده خوانده می‌شود نه از فایل. اگر نیاز دارید فهرست را پاک کنید ولی متن را نگه دارید، آرشیو با UUID همان کار است و به نام نیازی ندارد.

اندازه‌ی چیزی که با آن کار می‌کنید هم کوچک نیست. روی همین سرور، بدون آرشیو کردن چیزی، اندازه‌های واقعی این‌ها بودند: ۲۴ فایل نشست و مجموع ۱۱۶۰۰۰۷ بایت، یعنی ۱٫۱ مگابایت. کوچک‌ترین فایل ۳۷۷۳۷ بایت و بزرگ‌ترین ۱۰۶۷۱۷ بایت بود. میانگین ساده‌ی همین ۲۴ فایل می‌شود ۴۸۳۳۴ بایت، یعنی حدود ۴۸ کیلوبایت برای هر نشست، و این عدد از تقسیم ۱۱۶۰۰۰۷ بر ۲۴ به دست آمده است.

تقسیم بر حسب ماه نشان می‌دهد نشست‌ها یکنواخت پخش نشده‌اند: یک فایل در سپتامبر و ۲۳ فایل در اکتبر. پوشه‌ی archived_sessions روی این سرور اصلا وجود نداشت، یعنی تا این لحظه هیچ‌کس از این سه فرمان استفاده نکرده بود. همه‌ی عددهای این پست، عددهای همین لحظه‌ی خواندن در ۶ اکتبر ۲۰۲۶ است و روی ماشین شما فرق می‌کند.

برای دیدن وضعیت خودتان، همان درخت فایلی را از سمت مهاجرت هم باز کرده‌ایم در پست چطور با codex migrate-rollouts تاریخ نشست‌های قدیمی را برگردانیم. برای دیدن اینکه خود کدکس از یک نشست چه چیزی نگه می‌دارد، پست چطور ببینیم کدکس پیش از پیام ما چه چیزی به مدل تزریق می‌کند همان پایگاه‌داده‌ی state_5.sqlite را از سمت دیگر باز می‌کند.

منابع

  1. جدول فرمان‌ها و پرچم‌های خط فرمان کدکس — هر سه فرمان در فهرست فرمان‌ها آمده‌اند؛ خوانده در ۶ اکتبر ۲۰۲۶
  2. مرجع فرمان‌های توسعه‌دهنده‌ی کدکس، بخش archive و unarchive و delete — جمله‌ی «مرتب‌کردن فهرست نشست‌ها بدون حذف رونوشت» از همین‌جاست
  3. تعریف سه فرمان در کد کدکس، codex-rs/cli/src/main.rs — سطرهای ۲۰۷، ۲۱۰ و ۲۱۶؛ خوانده در ۶ اکتبر ۲۰۲۶
  4. انتشار rust-v0.134.0 در ۲۶ مه ۲۰۲۶ — تاریخ انتشار از همین صفحه خوانده شد
  5. انتشار rust-v0.136.0 در ۱ ژوئن ۲۰۲۶ — نخستین برچسبی که در کد، archive و unarchive دارد
  6. انتشار rust-v0.140.0 در ۱۵ ژوئن ۲۰۲۶ — نخستین برچسبی که در کد، delete را هم دارد
  7. انتشار rust-v0.155.0 در ۱۷ سپتامبر ۲۰۲۶ — پی‌آر 44433، افزودن کنش‌های آرشیو و حذف به نمای کلی نشست‌ها
  8. انتشار rust-v0.158.0 در ۲۸ سپتامبر ۲۰۲۶ — نسخه‌ای که روی این سرور نصب است
  9. انتشار rust-v0.160.1 در ۵ اکتبر ۲۰۲۶ — تازه‌ترین نسخه‌ی منتشرشده روی npm در لحظه‌ی خواندن
  10. صفحه‌ی بسته‌ی codex در npm — تازه‌ترین نسخه‌ی منتشرشده 0.160.1 است
  11. فهرست تغییرات کدکس — برای تطبیق تاریخ‌های انتشار