Skip to content

fix(conversations): resume opencode provider sessions#2201

Open
janburzinski wants to merge 2 commits into
mainfrom
cursor/972a0e10
Open

fix(conversations): resume opencode provider sessions#2201
janburzinski wants to merge 2 commits into
mainfrom
cursor/972a0e10

Conversation

@janburzinski
Copy link
Copy Markdown
Collaborator

summary

  • before we just did "--continue" which would continue the last opencode session
  • this now resumes the correct session in opencode

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 23, 2026

Greptile Summary

This PR fixes OpenCode session resumption by replacing the global --continue flag with --session <ses_id>, capturing the provider-native session ID from session.created hook events and persisting it per-conversation in the config column.

  • Adds a new session.created hook in the OpenCode JS plugin that captures the root session ID (skipping child sessions via parentID check) and posts it to the hook server; a new handleProviderSessionHook stores it in the DB via saveProviderSessionId.
  • Introduces resolveAgentSessionCommandArgs which, on resume, substitutes the stored ses_* ID for the conversation ID when available, and gracefully falls back to a fresh start when no session ID has been recorded yet.
  • Refactors config serialisation/parsing into a new shared conversation-config.ts module, replacing ad-hoc JSON.parse/stringify on autoApprove.

Confidence Score: 4/5

The change is safe to merge; the happy path is well-tested and the fallback behaviour (fresh start when no session ID is stored) is deliberately documented.

The core logic is sound: session IDs are captured via the new plugin hook, stored idempotently, and consumed correctly on resume. The two minor concerns are a lenient regex that could technically store a bare ses_ prefix and a silent fallback that gives no log signal when resumption degrades to a fresh start — neither affects the common case.

src/shared/conversation-config.ts (regex) and src/main/core/conversations/resolve-agent-session-command.ts (silent fallback) are the only spots worth a second look.

Important Files Changed

Filename Overview
src/main/core/agent-hooks/opencode-notifications-plugin.js Adds session.created handler that extracts root session ID; parentID guard correctly skips sub-sessions.
src/main/core/conversations/save-provider-session-id.ts Persists OpenCode session ID with idempotency check; validation relies on a lenient /^ses_/ regex.
src/main/core/conversations/resolve-agent-session-command.ts Resolves session args for OpenCode: uses stored ses_* ID when available, silently falls back to fresh start otherwise.
src/shared/conversation-config.ts New module for structured config parse/serialize; typed extraction replaces ad-hoc JSON.parse usage.
src/shared/agent-provider-registry.ts OpenCode entry updated: resumeFlag changed to --session, sessionIdFlag and sessionIdOnResumeOnly added.
src/main/core/conversations/impl/local-conversation.ts Delegates session/resume args to resolveAgentSessionCommandArgs before building the command.
src/main/core/conversations/impl/ssh-conversation.ts Mirrors local-conversation: uses resolved session args; resume flag also updated to agentSession.isResuming.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant LC as LocalConversation
    participant RA as resolveAgentSessionCommandArgs
    participant OC as OpenCode CLI
    participant HS as HookServer
    participant HH as handleProviderSessionHook
    participant DB as Database

    U->>LC: Resume conversation
    LC->>DB: getConversation (reads providerSessionId from config)
    LC->>RA: "resolveAgentSessionCommandArgs(conversation, isResuming=true)"
    alt providerSessionId stored
        RA-->>LC: "{ sessionId: ses_abc123, isResuming: true }"
        LC->>OC: opencode --session ses_abc123
    else no providerSessionId
        RA-->>LC: "{ sessionId: conv-1, isResuming: false }"
        LC->>OC: opencode (fresh start)
    end
    OC->>HS: POST /hook session.created (no parentID)
    HS->>HH: handleProviderSessionHook(raw)
    HH->>DB: saveProviderSessionId(conversationId, ses_abc123)
    DB-->>HH: updated config
    HS-->>OC: 200 OK
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
src/shared/conversation-config.ts:1
The `isOpenCodeProviderSessionId` guard accepts any string that starts with `ses_`, including `ses_` itself (4 characters, non-empty). A bare `ses_` prefix with no body would pass the `length === 0` check in `handleProviderSessionHook`, pass this regex, be stored in the DB, and then be forwarded as `opencode --session ses_` — which would likely cause OpenCode to fail with an unknown or malformed session. Adding a minimum-length requirement (e.g. `ses_` + at least one character) closes this gap without breaking any real IDs.

```suggestion
const OPENCODE_SESSION_ID_PATTERN = /^ses_.+/;
```

### Issue 2 of 2
src/main/core/conversations/resolve-agent-session-command.ts:11-12
**Silent fresh-start fallback has no observability**

When an OpenCode conversation is resumed but `providerSessionId` is not yet stored (e.g. the `session.created` webhook was never received), the function quietly downgrades to a fresh session. A user who clicks "Resume" would see a brand-new chat with no indication that resumption was skipped. Adding a `log.warn` here (similar to the warning in `saveProviderSessionId`) would make this case visible in logs without changing the user-facing behaviour.

Reviews (1): Last reviewed commit: "fix(conversations): resume opencode prov..." | Re-trigger Greptile

Comment thread src/shared/conversation-config.ts Outdated
@@ -0,0 +1,32 @@
const OPENCODE_SESSION_ID_PATTERN = /^ses_/;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The isOpenCodeProviderSessionId guard accepts any string that starts with ses_, including ses_ itself (4 characters, non-empty). A bare ses_ prefix with no body would pass the length === 0 check in handleProviderSessionHook, pass this regex, be stored in the DB, and then be forwarded as opencode --session ses_ — which would likely cause OpenCode to fail with an unknown or malformed session. Adding a minimum-length requirement (e.g. ses_ + at least one character) closes this gap without breaking any real IDs.

Suggested change
const OPENCODE_SESSION_ID_PATTERN = /^ses_/;
const OPENCODE_SESSION_ID_PATTERN = /^ses_.+/;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/shared/conversation-config.ts
Line: 1

Comment:
The `isOpenCodeProviderSessionId` guard accepts any string that starts with `ses_`, including `ses_` itself (4 characters, non-empty). A bare `ses_` prefix with no body would pass the `length === 0` check in `handleProviderSessionHook`, pass this regex, be stored in the DB, and then be forwarded as `opencode --session ses_` — which would likely cause OpenCode to fail with an unknown or malformed session. Adding a minimum-length requirement (e.g. `ses_` + at least one character) closes this gap without breaking any real IDs.

```suggestion
const OPENCODE_SESSION_ID_PATTERN = /^ses_.+/;
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +11 to +12
}
return { sessionId: conversation.id, isResuming: false };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Silent fresh-start fallback has no observability

When an OpenCode conversation is resumed but providerSessionId is not yet stored (e.g. the session.created webhook was never received), the function quietly downgrades to a fresh session. A user who clicks "Resume" would see a brand-new chat with no indication that resumption was skipped. Adding a log.warn here (similar to the warning in saveProviderSessionId) would make this case visible in logs without changing the user-facing behaviour.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/main/core/conversations/resolve-agent-session-command.ts
Line: 11-12

Comment:
**Silent fresh-start fallback has no observability**

When an OpenCode conversation is resumed but `providerSessionId` is not yet stored (e.g. the `session.created` webhook was never received), the function quietly downgrades to a fresh session. A user who clicks "Resume" would see a brand-new chat with no indication that resumption was skipped. Adding a `log.warn` here (similar to the warning in `saveProviderSessionId`) would make this case visible in logs without changing the user-facing behaviour.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant