From 1f001fd0572985f164ce327a09cf54df3dd6af32 Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Thu, 2 Apr 2026 00:07:18 +0000 Subject: [PATCH] docs: add opencode session internals documentation Document findings from database investigation: - Session table schema with all fields explained - Session ID format and generation (unique, no duplicates) - Parent-child relationships for forked sessions - Session detection logic used by kugetsu - Permission structure and common issues - SQL queries for debugging session problems - Known issues and solutions (from #81, #36) This document helps future debugging of session-related issues without having to investigate opencode internals directly. --- docs/opencode-session-internals.md | 247 +++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 docs/opencode-session-internals.md diff --git a/docs/opencode-session-internals.md b/docs/opencode-session-internals.md new file mode 100644 index 0000000..dde6e07 --- /dev/null +++ b/docs/opencode-session-internals.md @@ -0,0 +1,247 @@ +# OpenCode Session Internals + +This document contains findings about how OpenCode manages sessions, based on direct database investigation. Use this when debugging session-related issues in kugetsu. + +## Database Location + +```bash +opencode db path +# Returns: ~/.local/share/opencode/opencode.db +``` + +## Session Table Schema + +```sql +CREATE TABLE `session` ( + `id` text PRIMARY KEY, + `project_id` text NOT NULL, + `parent_id` text, -- Parent session ID (for forked sessions) + `slug` text NOT NULL, -- Auto-generated adjective-animal name + `directory` text NOT NULL, -- Working directory for session + `title` text NOT NULL, + `version` text NOT NULL, + `share_url` text, + `summary_additions` integer, + `summary_deletions` integer, + `summary_files` integer, + `summary_diffs` text, + `revert` text, + `permission` text, -- JSON array of permission rules + `time_created` integer NOT NULL, -- Unix timestamp in milliseconds + `time_updated` integer NOT NULL, + `time_compacting` integer, + `time_archived` integer, + `workspace_id` text +); +``` + +## Session ID Format + +OpenCode session IDs follow the format: `ses_` + +Example: `ses_2b4eb7afbffezJwifgucdLRkt8` + +The ID appears to be generated using a timestamp-based algorithm with random components. Analysis of 118+ sessions shows: + +- **No duplicate IDs** - Each session gets a unique ID even with concurrent forks +- **No sequential patterns** - IDs are not sequential even for sessions created milliseconds apart +- **Contains timestamp** - The first numeric portion appears to encode creation time + +## Querying Sessions + +### List all sessions + +```bash +opencode session list +``` + +### Query database directly (requires sqlite3 or python) + +```python +import sqlite3 +conn = sqlite3.connect('/home/shoko/.local/share/opencode/opencode.db') +cursor = conn.cursor() + +# Get all sessions +cursor.execute('SELECT id, parent_id, slug, directory FROM session') + +# Get forked sessions (sessions with a parent) +cursor.execute('SELECT id, parent_id FROM session WHERE parent_id IS NOT NULL') + +# Get sessions by directory +cursor.execute("SELECT id, slug FROM session WHERE directory LIKE '%kugetsu%'") +``` + +## Session Relationships + +### Parent-Child Relationships + +When you run `opencode run --fork --session `, OpenCode: + +1. Creates a NEW session with a unique ID +2. Sets the `parent_id` field to reference the parent session +3. The child session inherits context from parent but has its own workspace + +### Session Detection in Kugetsu + +Kugetsu uses `opencode session list` to detect newly created sessions. The output format is: + +``` +ses_abc123def456 +ses_xyz789... +``` + +Kugetsu's `cmd_start` workflow: + +1. **Before fork**: List all sessions, store in array +2. **Fork**: Run `opencode run --fork --session ` +3. **After fork**: List sessions again +4. **Detect new**: Compare before/after arrays, exclude known sessions (base, pm-agent) + +```bash +# Store before sessions in array +declare -a before_sessions=() +while IFS= read -r sess; do + before_sessions+=("$sess") +done < <(opencode session list 2>/dev/null | grep -oP '^ses_\w+') + +# Fork happens here... + +# Find sessions not in before array +while IFS= read -r sess; do + # Skip base and pm-agent sessions + [ "$sess" = "$base_session_id"" ] && continue + [ "$sess" = "$pm_agent_session_id" ] && continue + + # Check if session existed before + local existed_before=false + for before_sess in "${before_sessions[@]}"; do + if [ "$sess" = "$before_sess" ]; then + existed_before=true + break + fi + done + + if [ "$existed_before" = false ]; then + new_session_id="$sess" + break + fi +done < <(opencode session list 2>/dev/null | grep -oP '^ses_\w+') +``` + +## Session Directories + +Each session has a `directory` field indicating its working directory: + +| Directory | Purpose | +|-----------|---------| +| `/home/shoko` | Base session, PM agent | +| `/home/shoko/repositories/kugetsu` | Project sessions | +| `~/.kugetsu/worktrees/` | Per-issue worktrees | + +## Permissions + +Sessions have a `permission` field containing a JSON array: + +```json +[ + {"permission": "question", "pattern": "*", "action": "deny"}, + {"permission": "plan_enter", "pattern": "*", "action": "deny"}, + {"permission": "plan_exit", "pattern": "*", "action": "deny"}, + {"permission": "external_directory", "pattern": "*", "action": "allow"} +] +``` + +### Common Permission Issues + +**Issue**: `permission requested: external_directory (/path/*); auto-rejecting` + +**Cause**: The session's `permission` field may be `NULL` or missing required rules. + +**Fix**: Update via SQLite: + +```python +import sqlite3 +conn = sqlite3.connect('/home/shoko/.local/share/opencode/opencode.db') +cursor = conn.cursor() + +PERMISSION_JSON = '[{"permission":"question","pattern":"*","action":"deny"},{"permission":"plan_enter","pattern":"*","action":"deny"},{"permission":"plan_exit","pattern":"*","action":"deny"},{"permission":"external_directory","pattern":"*","action":"allow"}]' + +cursor.execute("UPDATE session SET permission = ? WHERE id = ?", + (PERMISSION_JSON, session_id)) +conn.commit() +``` + +## Known Issues & Solutions + +### Session ID Collision (Issue #81) + +**Problem**: Forked sessions showing same ID as PM agent. + +**Investigation Results**: +- OpenCode does NOT generate duplicate IDs (verified with 118+ sessions) +- Database shows unique IDs even for concurrent forks +- Issue is in kugetsu's session detection logic, not opencode + +**Solution**: Use array-based session detection (see above) instead of string/regex matching. + +### Stale Permission NULL (Issue #36) + +**Problem**: PM agent cannot access directories despite permissions. + +**Root Cause**: Session created with `permission = NULL` in database. + +**Detection**: +```python +cursor.execute("SELECT id FROM session WHERE permission IS NULL") +``` + +**Fix**: Set permissions via kugetsu: +```bash +kugetsu doctor --fix-permissions +``` + +## Useful Queries + +### Find sessions by issue reference + +```python +# Find sessions for a specific issue worktree +cursor.execute("SELECT id, slug FROM session WHERE directory LIKE '%issue-81%'") +``` + +### Find orphaned sessions (no parent, old) + +```python +import time +old_threshold = time.time() - (30 * 24 * 60 * 60) # 30 days ago + +cursor.execute("""SELECT id, slug, directory, time_created + FROM session + WHERE parent_id IS NULL + AND time_created < ? + ORDER BY time_created""", (old_threshold * 1000,)) +``` + +### Count sessions per project + +```python +cursor.execute("""SELECT project_id, COUNT(*) as cnt + FROM session + GROUP BY project_id + ORDER BY cnt DESC""") +``` + +## Debugging Tips + +1. **Check current sessions**: `opencode session list` +2. **Check database**: `opencode db "SELECT id, parent_id, slug FROM session ORDER BY time_created DESC LIMIT 10"` +3. **Verify permissions**: Check if `permission` field is NULL or valid JSON +4. **Check directory**: Ensure session directory exists and is accessible +5. **Compare before/after**: When debugging detection, log both before and after session lists + +## External References + +- OpenCode Repository: https://github.com/opencode-ai/opencode +- Session Management: Uses SQLite with unique constraint on `id` column +- Fork Operation: Sets `parent_id` to establish relationship