- หน้าแรก
- Documentation
- เซสชัน
- การสลับเซสชันและการแสดงรายการเซสชันล่าสุด
การสลับเซสชันและการแสดงรายการเซสชันล่าสุด
เอกสารนี้อธิบายวิธีที่ coding-agent ค้นพบเซสชันล่าสุด แก้ไขเป้าหมาย --resume แสดงตัวเลือกเซสชัน และสลับเซสชันรันไทม์ที่กำลังใช้งานอยู่
เนื้อหาเน้นไปที่พฤติกรรมการใช้งานปัจจุบัน รวมถึงเส้นทางสำรองและข้อควรระวังต่างๆ
ไฟล์การใช้งาน
หัวข้อที่มีชื่อว่า “ไฟล์การใช้งาน”../src/session/session-manager.ts../src/session/agent-session.ts../src/cli/session-picker.ts../src/modes/components/session-selector.ts../src/modes/controllers/selector-controller.ts../src/main.ts../src/sdk.ts../src/modes/interactive-mode.ts../src/modes/utils/ui-helpers.ts
การค้นพบเซสชันล่าสุด
หัวข้อที่มีชื่อว่า “การค้นพบเซสชันล่าสุด”ขอบเขตไดเรกทอรี
หัวข้อที่มีชื่อว่า “ขอบเขตไดเรกทอรี”SessionManager จัดเก็บเซสชันภายใต้ไดเรกทอรีที่กำหนดขอบเขตตาม cwd โดยค่าเริ่มต้น:
~/.xcsh/agent/sessions/--<cwd-encoded>--/*.jsonl
SessionManager.list(cwd, sessionDir?) อ่านเฉพาะไดเรกทอรีนั้น เว้นแต่จะมีการระบุ sessionDir อย่างชัดเจน
สองเส้นทางการแสดงรายการที่มีข้อมูลต่างกัน
หัวข้อที่มีชื่อว่า “สองเส้นทางการแสดงรายการที่มีข้อมูลต่างกัน”มีสองไปป์ไลน์การแสดงรายการที่แตกต่างกัน:
-
getRecentSessions(sessionDir, limit)(มุมมองต้อนรับ/สรุป)- อ่านเฉพาะส่วนนำขนาด 4KB (
readTextPrefix(..., 4096)) จากแต่ละไฟล์ - แยกวิเคราะห์ส่วนหัวและข้อความแสดงตัวอย่างของผู้ใช้ในช่วงต้น
- คืนค่า
RecentSessionInfoแบบเบาพร้อม getter แบบ lazy สำหรับnameและtimeAgo - เรียงลำดับตาม
mtimeของไฟล์จากมากไปน้อย
- อ่านเฉพาะส่วนนำขนาด 4KB (
-
SessionManager.list(...)/SessionManager.listAll()(ตัวเลือกสำหรับ resume และการจับคู่ ID)- อ่านไฟล์เซสชันทั้งหมด
- สร้างออบเจ็กต์
SessionInfo(id,cwd,title,messageCount,firstMessage,allMessagesText, timestamps) - ตัดทิ้งเซสชันที่มีรายการ
messageเป็นศูนย์ - เรียงลำดับตาม
modifiedจากมากไปน้อย
พฤติกรรมสำรองของข้อมูลเมตา
หัวข้อที่มีชื่อว่า “พฤติกรรมสำรองของข้อมูลเมตา”สำหรับสรุปล่าสุด (RecentSessionInfo):
- ลำดับความสำคัญของชื่อที่แสดง:
header.title-> prompt แรกของผู้ใช้ ->header.id-> ชื่อไฟล์ - ชื่อถูกตัดให้เหลือ 40 ตัวอักษรสำหรับการแสดงผลแบบกระชับ
- อักขระควบคุมและการขึ้นบรรทัดใหม่จะถูกลบ/ทำความสะอาดจากชื่อที่ได้มาจากชื่อเรื่อง
สำหรับรายการ SessionInfo:
titleคือheader.titleหรือshortSummaryจากการบีบอัดล่าสุดfirstMessageคือข้อความของข้อความแรกจากผู้ใช้ หรือ"(no messages)"
การแก้ไข --continue และการให้ความสำคัญกับ breadcrumb ของเทอร์มินัล
หัวข้อที่มีชื่อว่า “การแก้ไข --continue และการให้ความสำคัญกับ breadcrumb ของเทอร์มินัล”SessionManager.continueRecent(cwd, sessionDir?) แก้ไขเป้าหมายตามลำดับดังนี้:
- อ่าน breadcrumb ที่กำหนดขอบเขตตามเทอร์มินัล (
~/.xcsh/agent/terminal-sessions/<terminal-id>) - ตรวจสอบ breadcrumb:
- สามารถระบุเทอร์มินัลปัจจุบันได้
- cwd ของ breadcrumb ตรงกับ cwd ปัจจุบัน (เปรียบเทียบ resolved path)
- ไฟล์ที่อ้างอิงยังคงมีอยู่
- หาก breadcrumb ไม่ถูกต้อง/ขาดหายไป ให้ใช้ไฟล์ล่าสุดตาม mtime ในไดเรกทอรีเซสชันแทน (
findMostRecentSession) - หาไม่พบ ให้สร้างเซสชันใหม่
การหา Terminal ID จะให้ความสำคัญกับเส้นทาง TTY และใช้ตัวระบุจากสภาพแวดล้อมเป็นทางเลือกสำรอง (KITTY_WINDOW_ID, TMUX_PANE, TERM_SESSION_ID, WT_SESSION)
การเขียน breadcrumb เป็นการดำเนินการแบบ best-effort และไม่ทำให้เกิดข้อผิดพลาดร้ายแรง
การแก้ไขเป้าหมาย resume ณ เวลาเริ่มต้น (main.ts)
หัวข้อที่มีชื่อว่า “การแก้ไขเป้าหมาย resume ณ เวลาเริ่มต้น (main.ts)”--resume <value>
หัวข้อที่มีชื่อว่า “--resume <value>”createSessionManager(...) จัดการ --resume ที่มีค่าเป็น string ใน 2 โหมด:
-
ค่าที่มีลักษณะเป็น path (มี
/,\\, หรือลงท้ายด้วย.jsonl)- เปิดโดยตรงด้วย
SessionManager.open(sessionArg, parsed.sessionDir)
- เปิดโดยตรงด้วย
-
ค่าที่เป็น ID prefix
- ค้นหาการจับคู่ใน
SessionManager.list(cwd, sessionDir)โดยใช้id.startsWith(sessionArg) - หาไม่พบในพื้นที่และไม่ได้บังคับ
sessionDirให้ลองSessionManager.listAll() - ใช้การจับคู่แรกที่พบ (ไม่มีการถามยืนยันเมื่อพบหลายรายการ)
- ค้นหาการจับคู่ใน
พฤติกรรมเมื่อพบการจับคู่ข้ามโปรเจกต์:
- หาก cwd ของเซสชันที่จับคู่ได้แตกต่างจาก cwd ปัจจุบัน CLI จะถามว่าต้องการ fork ไปยังโปรเจกต์ปัจจุบันหรือไม่
- ใช่ ->
SessionManager.forkFrom(...) - ไม่ -> โยน error (
Session "..." is in another project (...))
ไม่พบการจับคู่ -> โยน error (Session "..." not found.)
--resume (ไม่มีค่า)
หัวข้อที่มีชื่อว่า “--resume (ไม่มีค่า)”จัดการหลังจากการสร้าง session-manager เริ่มต้น:
- แสดงรายการเซสชันในพื้นที่ด้วย
SessionManager.list(cwd, parsed.sessionDir) - หากว่าง: แสดง
No sessions foundและออกจากโปรแกรมก่อน - เปิดตัวเลือก TUI (
selectSession) - หากยกเลิก: แสดง
No session selectedและออกจากโปรแกรมก่อน - หากเลือก:
SessionManager.open(selectedPath)
--continue
หัวข้อที่มีชื่อว่า “--continue”ใช้ SessionManager.continueRecent(...) โดยตรง (พฤติกรรม breadcrumb-first ดังที่อธิบายข้างต้น)
กลไกภายในของการเลือกผ่านตัวเลือก
หัวข้อที่มีชื่อว่า “กลไกภายในของการเลือกผ่านตัวเลือก”ตัวเลือก CLI (src/cli/session-picker.ts)
หัวข้อที่มีชื่อว่า “ตัวเลือก CLI (src/cli/session-picker.ts)”selectSession(sessions) สร้าง TUI แบบ standalone ด้วย SessionSelectorComponent และ resolve ครั้งเดียว:
- เลือก -> resolve path ที่เลือก
- ยกเลิก (Esc) -> resolve
null - ออกแบบบังคับ (เส้นทาง Ctrl+C) -> หยุด TUI และ
process.exit(0)
ตัวเลือกภายในเซสชันแบบ interactive (SelectorController.showSessionSelector)
หัวข้อที่มีชื่อว่า “ตัวเลือกภายในเซสชันแบบ interactive (SelectorController.showSessionSelector)”ขั้นตอนการทำงาน:
- ดึงเซสชันจากไดเรกทอรีเซสชันปัจจุบันผ่าน
SessionManager.list(currentCwd, currentSessionDir) - ติดตั้ง
SessionSelectorComponentในพื้นที่ editor โดยใช้showSelector(...) - callbacks:
- เลือก -> ปิดตัวเลือกและเรียก
handleResumeSession(sessionPath) - ยกเลิก -> คืนค่า editor และ rerender
- ออก ->
ctx.shutdown()
- เลือก -> ปิดตัวเลือกและเรียก
พฤติกรรมของส่วนประกอบตัวเลือกเซสชัน
หัวข้อที่มีชื่อว่า “พฤติกรรมของส่วนประกอบตัวเลือกเซสชัน”SessionList รองรับ:
- การนำทางด้วยลูกศร/หน้า
- Enter เพื่อเลือก
- Esc เพื่อยกเลิก
- Ctrl+C เพื่อออก
- การค้นหาแบบ fuzzy ข้ามข้อมูล id/title/cwd/first message/all messages/path ของเซสชัน
พฤติกรรมการแสดงผลเมื่อรายการว่าง:
- แสดงข้อความแทนที่จะเกิด crash
- การกด Enter บนรายการว่างไม่ทำอะไร (ไม่มี callback)
- Esc/Ctrl+C ยังคงทำงานได้
ข้อควรระวัง: ข้อความใน UI ระบุว่า Press Tab to view all แต่ส่วนประกอบนี้ยังไม่มี handler สำหรับ Tab ในปัจจุบัน และการเชื่อมต่อปัจจุบันแสดงรายการเฉพาะเซสชันในขอบเขตปัจจุบันเท่านั้น
การดำเนินการสลับรันไทม์ (AgentSession.switchSession)
หัวข้อที่มีชื่อว่า “การดำเนินการสลับรันไทม์ (AgentSession.switchSession)”switchSession(sessionPath) คือเส้นทางหลักในการสลับภายในกระบวนการ
วงจรชีวิต/การเปลี่ยนสถานะ:
- บันทึก
previousSessionFile - ส่ง event hook
session_before_switch(reason: "resume", สามารถยกเลิกได้) - หากยกเลิก -> คืนค่า
falseโดยไม่สลับ - ตัดการเชื่อมต่อจาก event stream ของ agent ปัจจุบัน
- ยกเลิกการสร้าง/กระบวนการ tool ที่กำลังทำงาน
- ล้าง buffer ข้อความ steering/follow-up/next-turn ที่รอคิว
- flush session writer (
sessionManager.flush()) เพื่อบันทึกการเขียนที่รอดำเนินการ sessionManager.setSessionFile(sessionPath)- อัปเดต pointer ไฟล์เซสชัน
- เขียน terminal breadcrumb
- โหลด entries / migrate / blob-resolve / reindex
- หากข้อมูลไฟล์ขาดหาย/ไม่ถูกต้อง: เริ่มต้นเซสชันใหม่ที่ path นั้นและเขียนส่วนหัวใหม่
- อัปเดต
agent.sessionId - สร้าง context ใหม่ผ่าน
buildSessionContext() - ส่ง event hook
session_switch(reason: "resume",previousSessionFile) - แทนที่ข้อความ agent ด้วย context ที่สร้างใหม่
- คืนค่า model เริ่มต้นจาก
sessionContext.models.defaultหากมีและอยู่ใน model registry - คืนค่าระดับ thinking:
- หาก branch มี
thinking_level_changeอยู่แล้ว ให้ใช้ระดับเซสชันที่บันทึกไว้ - มิฉะนั้น ให้หาระดับ thinking เริ่มต้นจาก settings, จำกัดให้อยู่ในความสามารถของ model, ตั้งค่า และเพิ่มรายการ
thinking_level_changeใหม่
- หาก branch มี
- เชื่อมต่อ agent listeners อีกครั้งและคืนค่า
true
การสร้างสถานะ UI ใหม่หลังการสลับแบบ interactive
หัวข้อที่มีชื่อว่า “การสร้างสถานะ UI ใหม่หลังการสลับแบบ interactive”SelectorController.handleResumeSession ดำเนินการรีเซ็ต UI รอบๆ switchSession:
- หยุดแอนิเมชันการโหลด
- ล้าง status container
- ล้าง pending-message UI และ pending tool map
- รีเซ็ต streaming component/message references
- เรียก
session.switchSession(...) - ล้าง chat container และ rerender จาก session context (
renderInitialMessages) - โหลด todos ใหม่จาก artifacts ของเซสชันใหม่
- แสดง
Resumed session
ดังนั้นสถานะการสนทนา/todo ที่มองเห็นได้จะถูกสร้างใหม่จากไฟล์เซสชันใหม่
การ resume ขณะเริ่มต้น vs การสลับภายในเซสชัน
หัวข้อที่มีชื่อว่า “การ resume ขณะเริ่มต้น vs การสลับภายในเซสชัน”การ resume ขณะเริ่มต้น (--continue, --resume, การเปิดโดยตรง)
หัวข้อที่มีชื่อว่า “การ resume ขณะเริ่มต้น (--continue, --resume, การเปิดโดยตรง)”- เลือกไฟล์เซสชันก่อน
createAgentSession(...) sdk.tsสร้างexistingSession = sessionManager.buildSessionContext()- ข้อความ agent ถูกคืนค่าครั้งเดียวระหว่างการสร้างเซสชัน
- Model/thinking ถูกเลือกระหว่างการสร้าง (รวมถึง logic การคืนค่า/fallback)
- โหมด interactive จากนั้นรัน
#restoreModeFromSession()เพื่อกลับเข้าสู่สถานะโหมดที่บันทึกไว้ (ปัจจุบันคือ plan/plan_paused)
การสลับภายในเซสชัน (เส้นทางตัวเลือกแบบ /resume)
หัวข้อที่มีชื่อว่า “การสลับภายในเซสชัน (เส้นทางตัวเลือกแบบ /resume)”- ใช้
AgentSession.switchSession(...)บนAgentSessionที่กำลังทำงานอยู่แล้ว - ข้อความ/model/thinking ถูกสร้างใหม่ทันทีในที่เดิม
- ส่ง event hook
session_before_switch/session_switch - UI chat/todos ถูกรีเฟรช
- ไม่มีการเรียก mode restore หลังการสลับโดยเฉพาะในเส้นทาง selector; พฤติกรรมการกลับเข้าสู่โหมดไม่สมมาตรกับ
#restoreModeFromSession()ขณะเริ่มต้น
พฤติกรรมเมื่อเกิดความล้มเหลวและกรณีขอบ
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดความล้มเหลวและกรณีขอบ”เส้นทางการยกเลิก
หัวข้อที่มีชื่อว่า “เส้นทางการยกเลิก”- ยกเลิกตัวเลือก CLI -> คืนค่า
null, caller แสดงNo session selected, กระบวนการออกก่อน - ยกเลิกตัวเลือกแบบ interactive -> คืนค่า editor โดยไม่มีการเปลี่ยนเซสชัน
- การยกเลิกผ่าน hook (
session_before_switch) ->switchSession()คืนค่าfalse
เส้นทางรายการว่าง
หัวข้อที่มีชื่อว่า “เส้นทางรายการว่าง”- CLI
--resume(ไม่มีค่า): รายการว่างแสดงNo sessions foundและออก - ตัวเลือกแบบ interactive: รายการว่างแสดงข้อความและยังคงสามารถยกเลิกได้
ไฟล์เซสชันเป้าหมายที่ขาดหาย/ไม่ถูกต้อง
หัวข้อที่มีชื่อว่า “ไฟล์เซสชันเป้าหมายที่ขาดหาย/ไม่ถูกต้อง”เมื่อเปิด/สลับไปยัง path เฉพาะ (setSessionFile):
- ENOENT -> ถือว่าว่าง -> เริ่มต้นเซสชันใหม่ที่ path นั้นและบันทึก
- ส่วนหัวที่ผิดรูปแบบ/ไม่ถูกต้อง (หรือรายการที่แยกวิเคราะห์ไม่ได้) -> ถือว่าว่าง -> เริ่มต้นเซสชันใหม่และบันทึก
นี่คือพฤติกรรมการกู้คืน ไม่ใช่ความล้มเหลวแบบร้ายแรง
ความล้มเหลวแบบร้ายแรง
หัวข้อที่มีชื่อว่า “ความล้มเหลวแบบร้ายแรง”การสลับ/เปิดยังคงสามารถโยน error จากความล้มเหลวของ I/O จริงๆ (ข้อผิดพลาดสิทธิ์การเข้าถึง, ความล้มเหลวในการเขียนใหม่ ฯลฯ) ซึ่งจะแพร่กระจายไปยัง caller
ข้อควรระวังในการจับคู่ ID prefix
หัวข้อที่มีชื่อว่า “ข้อควรระวังในการจับคู่ ID prefix”- การจับคู่ ID ใช้
startsWithและใช้การจับคู่แรกในรายการที่เรียงลำดับแล้ว - ไม่มี UI สำหรับการยืนยันหากเซสชันหลายรายการมี prefix เดียวกัน
SessionManager.list(...)ไม่รวมเซสชันที่มีข้อความเป็นศูนย์ ดังนั้นเซสชันเหล่านั้นจึงไม่สามารถ resume ได้ผ่านการจับคู่ ID/ตัวเลือกรายการ