Skip to content

Commit f0ce81d

Browse files
authored
fix: fix fork cleanup title matching (#16)
* fix: match fork cleanup by title suffix * test: remove sqlite3 cli dependency from wrapper tests
1 parent d6b5ad4 commit f0ce81d

2 files changed

Lines changed: 462 additions & 18 deletions

File tree

bin/opencode-memory

Lines changed: 119 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
476511
get_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+
837944
file_mtime_secs() {
838945
local file="$1"
839946
if [ ! -f "$file" ]; then
@@ -973,11 +1080,10 @@ rollback_consolidation_lock() {
9731080
}
9741081

9751082
cleanup_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

@@ -993,7 +1099,7 @@ PY
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

11391241
run_post_session_tasks() {

0 commit comments

Comments
 (0)