@@ -473,6 +473,41 @@ get_latest_session_id() {
473473 fi
474474}
475475
476+ get_opencode_db_path () {
477+ printf ' %s\n' " $HOME /.local/share/opencode/opencode.db"
478+ }
479+
480+ get_session_title_from_db () {
481+ local session_id=" $1 "
482+ local db_path
483+ db_path=$( get_opencode_db_path)
484+
485+ if [ -z " $session_id " ] || [ ! -f " $db_path " ] || ! command -v python3 > /dev/null 2>&1 ; then
486+ return 1
487+ fi
488+
489+ python3 - " $db_path " " $session_id " << 'PY '
490+ import sqlite3
491+ import sys
492+
493+ db_path, session_id = sys.argv[1:3]
494+
495+ try:
496+ conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
497+ row = conn.execute("SELECT title FROM session WHERE id = ? LIMIT 1", (session_id,)).fetchone()
498+ except Exception:
499+ raise SystemExit(1)
500+ finally:
501+ try:
502+ conn.close()
503+ except Exception:
504+ pass
505+
506+ if row and row[0]:
507+ print(row[0])
508+ PY
509+ }
510+
476511get_session_target_id () {
477512 local before_json=" $1 "
478513 local started_at_ms=" $2 "
@@ -834,6 +869,78 @@ wait_for_session_target_id() {
834869 wait_for_scoped_session_id_since " $before_json " " $started_at_ms " " $TIMESTAMP_FILE " " $wait_seconds "
835870}
836871
872+ get_fork_cleanup_candidate_id () {
873+ local started_at_ms=" $1 "
874+ local parent_title=" $2 "
875+ local workdir=" $3 "
876+ local project_dir=" $4 "
877+ local db_path
878+ db_path=$( get_opencode_db_path)
879+
880+ if [ -z " $started_at_ms " ] || [ -z " $parent_title " ] || [ ! -f " $db_path " ] || ! command -v python3 > /dev/null 2>&1 ; then
881+ return 1
882+ fi
883+
884+ python3 - " $db_path " " $started_at_ms " " $parent_title " " $workdir " " $project_dir " << 'PY '
885+ import os
886+ import re
887+ import sqlite3
888+ import sys
889+
890+ db_path, started_at_ms_raw, parent_title, workdir, project_dir = sys.argv[1:6]
891+ started_at_ms = int(started_at_ms_raw or "0")
892+ scope = {os.path.realpath(path) for path in (workdir, project_dir) if path}
893+ title_pattern = re.compile(rf"^{re.escape(parent_title)} \(fork #\d+\)$")
894+
895+ try:
896+ conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
897+ rows = conn.execute(
898+ "SELECT id, title, directory, time_created FROM session WHERE time_created >= ? ORDER BY time_created DESC",
899+ (started_at_ms,),
900+ ).fetchall()
901+ except Exception:
902+ raise SystemExit(1)
903+ finally:
904+ try:
905+ conn.close()
906+ except Exception:
907+ pass
908+
909+ matches = []
910+ for session_id, title, directory, time_created in rows:
911+ if not session_id or not title or not directory or not time_created:
912+ continue
913+ if os.path.realpath(directory) not in scope:
914+ continue
915+ if not title_pattern.match(title):
916+ continue
917+ matches.append(session_id)
918+
919+ if len(matches) == 1:
920+ print(matches[0])
921+ PY
922+ }
923+
924+ wait_for_fork_cleanup_candidate_id () {
925+ local started_at_ms=" $1 "
926+ local parent_title=" $2 "
927+ local wait_seconds=" ${3:- 5} "
928+ local attempt=0
929+ local session_id=" "
930+
931+ while [ " $attempt " -lt " $wait_seconds " ]; do
932+ session_id=$( get_fork_cleanup_candidate_id " $started_at_ms " " $parent_title " " $WORKING_DIR " " $PROJECT_SCOPE_DIR " || true)
933+ if [ -n " $session_id " ]; then
934+ printf ' %s\n' " $session_id "
935+ return 0
936+ fi
937+ sleep 1
938+ attempt=$(( attempt + 1 ))
939+ done
940+
941+ return 1
942+ }
943+
837944file_mtime_secs () {
838945 local file=" $1 "
839946 if [ ! -f " $file " ]; then
@@ -973,11 +1080,10 @@ rollback_consolidation_lock() {
9731080}
9741081
9751082cleanup_forked_sessions () {
976- local before_json=" $1 "
977- local started_at_ms=" $2 "
978- local timestamp_file=" $3 "
1083+ local started_at_ms=" $1 "
1084+ local parent_title=" $2 "
9791085
980- if [ -z " $before_json " ] || [ -z " $ started_at_ms" ] || [ -z " $timestamp_file " ]; then
1086+ if [ -z " $started_at_ms " ] || [ -z " $parent_title " ]; then
9811087 return 0
9821088 fi
9831089
9931099 fi
9941100
9951101 local fork_id
996- fork_id=$( wait_for_scoped_session_id_since " $before_json " " $ started_at_ms" " $timestamp_file " " $SESSION_WAIT_SECONDS " 0 || true)
1102+ fork_id=$( wait_for_fork_cleanup_candidate_id " $started_at_ms " " $parent_title " " $SESSION_WAIT_SECONDS " || true)
9971103
9981104 [ -n " $fork_id " ] || return 0
9991105
@@ -1040,6 +1146,9 @@ run_extraction_if_needed() {
10401146 log " Extracting memories from session $session_id ..."
10411147 log " Extraction log: $EXTRACT_LOG_FILE "
10421148
1149+ local parent_session_title
1150+ parent_session_title=$( get_session_title_from_db " $session_id " || true)
1151+
10431152 local cmd=(" $REAL_OPENCODE " run -s " $session_id " --fork --dir " $WORKING_DIR " )
10441153 if [ -n " $EXTRACT_MODEL " ]; then
10451154 cmd+=(-m " $EXTRACT_MODEL " )
@@ -1049,10 +1158,6 @@ run_extraction_if_needed() {
10491158 fi
10501159 cmd+=(" $EXTRACT_PROMPT " )
10511160
1052- local pre_fork_json
1053- pre_fork_json=$( get_session_list_json " $AUTODREAM_SCAN_LIMIT " 2> /dev/null || true)
1054- local fork_timestamp_file
1055- fork_timestamp_file=$( mktemp)
10561161 local fork_started_at_ms
10571162 fork_started_at_ms=$(( $(date +% s) * 1000 ))
10581163
@@ -1063,8 +1168,7 @@ run_extraction_if_needed() {
10631168 log " Memory extraction failed (exit code $code ). Check $EXTRACT_LOG_FILE for details"
10641169 fi
10651170
1066- cleanup_forked_sessions " $pre_fork_json " " $fork_started_at_ms " " $fork_timestamp_file "
1067- rm -f " $fork_timestamp_file "
1171+ cleanup_forked_sessions " $fork_started_at_ms " " $parent_session_title "
10681172 release_simple_lock " $EXTRACT_LOCK_FILE "
10691173}
10701174
@@ -1107,6 +1211,9 @@ run_autodream_if_needed() {
11071211 log " Auto-dream firing (${hours_since} h since last consolidation, ${touched_count} sessions touched)"
11081212 log " Auto-dream log: $AUTODREAM_LOG_FILE "
11091213
1214+ local parent_session_title
1215+ parent_session_title=$( get_session_title_from_db " $session_id " || true)
1216+
11101217 local cmd=(" $REAL_OPENCODE " run -s " $session_id " --fork --dir " $WORKING_DIR " )
11111218 if [ -n " $AUTODREAM_MODEL " ]; then
11121219 cmd+=(-m " $AUTODREAM_MODEL " )
@@ -1116,10 +1223,6 @@ run_autodream_if_needed() {
11161223 fi
11171224 cmd+=(" $AUTODREAM_PROMPT " )
11181225
1119- local pre_fork_json
1120- pre_fork_json=$( get_session_list_json " $AUTODREAM_SCAN_LIMIT " 2> /dev/null || true)
1121- local fork_timestamp_file
1122- fork_timestamp_file=$( mktemp)
11231226 local fork_started_at_ms
11241227 fork_started_at_ms=$(( $(date +% s) * 1000 ))
11251228
@@ -1132,8 +1235,7 @@ run_autodream_if_needed() {
11321235 rollback_consolidation_lock " $CONSOLIDATION_PRIOR_MTIME "
11331236 fi
11341237
1135- cleanup_forked_sessions " $pre_fork_json " " $fork_started_at_ms " " $fork_timestamp_file "
1136- rm -f " $fork_timestamp_file "
1238+ cleanup_forked_sessions " $fork_started_at_ms " " $parent_session_title "
11371239}
11381240
11391241run_post_session_tasks () {
0 commit comments