fix(attach): fetch on-demand transcripts for untracked sessions - #1877
Open
Legonaftik wants to merge 1 commit into
Open
fix(attach): fetch on-demand transcripts for untracked sessions#1877Legonaftik wants to merge 1 commit into
Legonaftik wants to merge 1 commit into
Conversation
Sessions spawned outside a hooked terminal (e.g. by an external session host) never get a hook-cached export under .entire/tmp, so resolveAndValidateTranscript gave up: PrepareTranscript is only called when the transcript file already exists, and OpenCode has no SessionBaseDirProvider fallback search. OpenCode can materialize any session's transcript via opencode export, tracked or not. Add a TranscriptFetcher capability (agent can fetch a transcript on demand), implement it on OpenCodeAgent via fetchAndCacheExport, and consult it in resolveAndValidateTranscript after the initial stat fails, before the project-dir search fallback. Refs entireio#1876
Legonaftik
force-pushed
the
fix/opencode-attach-fetch-untracked
branch
from
July 30, 2026 13:14
ab7bc82 to
36724c8
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes entire session attach <id> --agent opencode failing with “transcript not found” for OpenCode sessions that were never hook-tracked by allowing the OpenCode agent to materialize transcripts on demand during attach.
Changes:
- Introduces an optional
TranscriptFetcheragent capability and a helperAsTranscriptFetcherfor type-assertion-based discovery. - Implements
FetchTranscriptfor OpenCode via the existingfetchAndCacheExport(opencode export-backed). - Updates attach transcript resolution to attempt
FetchTranscriptafter the initial transcript-path stat fails, and adds regression/unit tests around the new behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| cmd/entire/cli/attach.go | Adds a fetch-on-demand fallback (via TranscriptFetcher) to transcript resolution during attach. |
| cmd/entire/cli/attach_test.go | Adds a regression test covering attach succeeding when OpenCode transcript must be fetched/materialized. |
| cmd/entire/cli/agent/opencode/lifecycle.go | Implements FetchTranscript using fetchAndCacheExport. |
| cmd/entire/cli/agent/opencode/lifecycle_test.go | Adds unit tests for FetchTranscript behavior (ID validation and export attempt). |
| cmd/entire/cli/agent/capabilities.go | Adds AsTranscriptFetcher optional capability resolver. |
| cmd/entire/cli/agent/agent.go | Defines the new TranscriptFetcher capability interface. |
Comment on lines
+813
to
+817
| // Agents that can materialize a transcript on demand (e.g. OpenCode via | ||
| // `opencode export`) can conjure one even when no hook-cached file exists, | ||
| // e.g. sessions spawned by an external host rather than a hooked terminal. | ||
| if fetcher, ok := agent.AsTranscriptFetcher(ag); ok { | ||
| fetched, fetchErr := fetcher.FetchTranscript(ctx, sessionID) |
Comment on lines
+402
to
+417
| func TestFetchTranscript_AttemptsExport(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| // Without mock-export mode, FetchTranscript must attempt `opencode export` | ||
| // — proving it tries to materialize the transcript for sessions with no | ||
| // hook-cached file rather than stat-checking an existing one. Without a | ||
| // usable session the export fails, wrapped as "opencode export failed". | ||
| ag := &OpenCodeAgent{} | ||
| _, err := ag.FetchTranscript(context.Background(), "test-fetch-transcript-no-such-session") | ||
| if err == nil { | ||
| t.Fatal("expected error for nonexistent session, got nil") | ||
| } | ||
| if !strings.Contains(err.Error(), "opencode export failed") { | ||
| t.Errorf("expected 'opencode export failed' error, got: %v", err) | ||
| } | ||
| } |
Comment on lines
+1907
to
+1913
| // TestAttach_OpenCodeFetchesTranscriptForUntrackedSession is a regression test | ||
| // for sessions spawned outside a hooked terminal (e.g. by an external session | ||
| // host): no hook ever cached an export under .entire/tmp, and | ||
| // resolveAndValidateTranscript used to give up because PrepareTranscript was | ||
| // only called when the file already existed. OpenCode can materialize the | ||
| // transcript via `opencode export`, so attach now consults the | ||
| // TranscriptFetcher capability before failing. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1876.
Problem
entire session attach <id> --agent opencodefails withtranscript not foundfor OpenCode sessions Entire never tracked — e.g. sessions spawned by an external session host rather than a hooked terminal.resolveAndValidateTranscriptonly callsPrepareTranscriptwhen the transcript file already exists under.entire/tmp, on the assumption that preparation "can't conjure a file that was never started". For OpenCode that assumption is false: its transcript is produced on demand byopencode export(fetchAndCacheExport), which works for any session in the opencode store, tracked or not. OpenCode also lacks aSessionBaseDirProvider, so the project-dir fallback can't help. Net effect: no way to attach the correct session when hooks didn't capture one.Fix
TranscriptFetcheragent capability (agent.go+AsTranscriptFetcher, type-assertion gated likeSidecarImageProvider): the agent can materialize a session transcript on demand, including for sessions with no hook-cached file.OpenCodeAgent.FetchTranscriptimplements it via the existingfetchAndCacheExport(which already honorsENTIRE_TEST_OPENCODE_MOCK_EXPORT).resolveAndValidateTranscriptconsults the capability after the initial stat fails, before the project-dir search fallback. File-based agents are unaffected.Out of scope (noted in the issue):
session listenumeration from the opencode store — the attach path was the workflow blocker.Testing
TestAttach_OpenCodeFetchesTranscriptForUntrackedSession: a session with no hook-cached export attaches successfully via the fetch path (mock-export mode), producing session state with a checkpoint id.TestFetchTranscript_ValidatesSessionID/TestFetchTranscript_AttemptsExportfor the new method.go test ./cmd/entire/cli/...andgolangci-lint runclean.Verification (downstream)
The same scenario was exercised end-to-end against a real externally-hosted OpenCode session (
opencode exportsucceeds, attach previously failed); with this change the fetch path materializes the transcript and attach proceeds.