Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/internal/session-format-sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,19 @@ add an archived or maintained mirror without replacing the original identity.

## OpenCode (`opencode`)

**Projection detail check (2026-09-12):** Rechecked the pinned
[assistant schema](https://github.com/anomalyco/opencode/blob/dff8fbc149fb7492e4f07b713ac31ea70d9a541c/packages/schema/src/session-message.ts),
[tool content schema](https://github.com/anomalyco/opencode/blob/dff8fbc149fb7492e4f07b713ac31ea70d9a541c/packages/schema/src/llm.ts),
and
[message updater](https://github.com/anomalyco/opencode/blob/dff8fbc149fb7492e4f07b713ac31ea70d9a541c/packages/core/src/session/message-updater.ts).
Step completion stores the assistant's `finish` value, which Agentsview now
retains as the parsed stop reason. Tool results can contain file items with a
URI, MIME type, and optional name. These retain attachment labels in result
text, using the MIME type when no name is present, without expanding the URI.
Isolated parser fixtures verify both mappings. These details extend the
existing v2 reader; session discovery and upgrade behavior are unchanged.
Data version 108 makes existing imports eligible to recover the result labels.

**V2 projection check (2026-09-08):** Cloned upstream at
`dff8fbc149fb7492e4f07b713ac31ea70d9a541c` and checked the
[SQL schema](https://github.com/anomalyco/opencode/blob/dff8fbc149fb7492e4f07b713ac31ea70d9a541c/packages/core/src/session/sql.ts),
Expand Down
4 changes: 3 additions & 1 deletion internal/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,9 @@ CREATE INDEX IF NOT EXISTS idx_provider_freshness_updated_at
// Subagent tool calls from Other to Task so delegation renders as a task call
// and leaves the Other analytics bucket; subagent transcripts themselves are
// new sources and need no re-parse.)
const dataVersion = 107
// (108: OpenCode v2 tool results retain file attachment labels. Existing
// sessions need re-parsing to recover labels omitted from stored results.)
const dataVersion = 108

const tokenCoverageRepairStatsKey = "token_coverage_repair_v1"

Expand Down
14 changes: 12 additions & 2 deletions internal/parser/opencode_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ type openCodeV2Message struct {
ShellID string `json:"shellID"`
Exit int `json:"exit"`
Status string `json:"status"`
Finish string `json:"finish"`
Files []struct {
Name string `json:"name"`
MIME string `json:"mime"`
Expand Down Expand Up @@ -186,6 +187,8 @@ type openCodeV2Content struct {
Content []struct {
Type string `json:"type"`
Text string `json:"text"`
Name string `json:"name"`
MIME string `json:"mime"`
} `json:"content"`
Error struct {
Message string `json:"message"`
Expand Down Expand Up @@ -236,7 +239,7 @@ func loadOpenCodeV2Messages(db *sql.DB, sessionID, cwd string) ([]ParsedMessage,
pm.Content += attachment
}
case "assistant":
pm.Role = RoleAssistant
pm.Role, pm.StopReason = RoleAssistant, data.Finish
var texts []string
for _, item := range data.Content {
switch item.Type {
Expand Down Expand Up @@ -313,8 +316,15 @@ func openCodeV2ToolCall(item openCodeV2Content, cwd string) ParsedToolCall {
if item.State.Status == "completed" || item.State.Status == "error" {
var texts []string
for _, content := range item.State.Content {
if content.Type == "text" {
switch content.Type {
case "text":
texts = append(texts, content.Text)
case "file":
name := content.Name
if name == "" {
name = content.MIME
}
texts = append(texts, "[Attachment: "+name+"]")
}
}
// The v2 read tool returns text files as structured UTF-8 attachments.
Expand Down
17 changes: 17 additions & 0 deletions internal/parser/opencode_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ func TestOpenCodeV2MixedDatabase(t *testing.T) {
func TestOpenCodeV2ToolStates(t *testing.T) {
for _, tc := range []struct{ name, tool, state, status, text string }{
{"structured", "custom", `{"status":"completed","input":{},"structured":{"content":[1,2],"exit":"other"},"content":[{"type":"text","text":"Done"}]}`, "completed", "Done"},
{"file output", "custom", `{"status":"completed","input":{},"structured":{},"content":[{"type":"text","text":"Created plot"},{"type":"file","uri":"data:image/png;base64,AAAA","mime":"image/png","name":"plot.png"},{"type":"file","uri":"data:application/pdf;base64,AAAA","mime":"application/pdf"}]}`, "completed", "Created plot\n[Attachment: plot.png]\n[Attachment: application/pdf]"},
{"pending", "bash", `{"status":"pending","input":"{\"command\":"}`, "", ""},
{"streaming", "shell", `{"status":"streaming","input":"{\"command\":"}`, "", ""},
{"running", "bash", `{"status":"running","input":{"command":"query"},"structured":{},"content":[]}`, "", ""},
Expand Down Expand Up @@ -345,6 +346,22 @@ func TestOpenCodeV2MessageKinds(t *testing.T) {
}
}

func TestOpenCodeV2StopReason(t *testing.T) {
path, seed, writer := newTestDB(t)
seed.AddProject("project-a", "/workspace/project-a")
seed.AddSession("ses_a", "project-a", "", "", 1700000000000, 1700000001000)
_, err := writer.Exec(openCodeV2TestSchema)
require.NoError(t, err)
_, err = writer.Exec(`INSERT INTO session_message VALUES ('msg_a', 'ses_a', 'assistant', 1, 1700000000000, 1700000001000,
'{"finish":"length","content":[{"type":"text","id":"txt_a","text":"Partial reply"}]}')`)
require.NoError(t, err)
_, msgs, err := parseOpenCodeDBSession(path, "ses_a", "host-a")
require.NoError(t, err)
require.Len(t, msgs, 1)
assert.Equal(t, "Partial reply", msgs[0].Content)
assert.Equal(t, "length", msgs[0].StopReason)
}

func TestOpenCodeV2CrossPathHistory(t *testing.T) {
// Reproduced with 1.18.25: the v2 API accepts an existing CLI session
// and appends projections without converting its older message/part rows.
Expand Down