git.iko.soy/opencode-android
on a0ce9ce help sign in
127 lines 3.9 kB history raw
1package soy.iko.opencode.data.model
3import androidx.compose.runtime.Immutable
4import kotlinx.serialization.SerialName
5import kotlinx.serialization.Serializable
6import kotlinx.serialization.json.JsonElement
7import kotlinx.serialization.json.JsonObject
8import kotlinx.serialization.json.JsonPrimitive
10/**
11 * A message part — a discriminated union on `type` (the global class discriminator).
12 * Unknown types decode to [UnknownPart] via the registered polymorphic default,
13 * so the stream survives server additions.
14 *
15 * Every part carries [id] plus its parent [messageID]/[sessionID], which the reducer
16 * uses to attach streamed parts to the right message.
17 */
18@Serializable
19sealed interface Part {
20 val id: String
21 val messageID: String?
22 val sessionID: String?
25@Immutable
26@Serializable
27@SerialName("text")
28data class TextPart(
29 override val id: String = "",
30 override val messageID: String? = null,
31 override val sessionID: String? = null,
32 val text: String = "",
33 val synthetic: Boolean = false,
34 val ignored: Boolean = false,
35 val time: PartTimeInfo? = null,
36) : Part
38@Immutable
39@Serializable
40@SerialName("reasoning")
41data class ReasoningPart(
42 override val id: String = "",
43 override val messageID: String? = null,
44 override val sessionID: String? = null,
45 val text: String = "",
46 val time: PartTimeInfo? = null,
47) : Part
49@Immutable
50@Serializable
51@SerialName("tool")
52data class ToolPart(
53 override val id: String = "",
54 override val messageID: String? = null,
55 override val sessionID: String? = null,
56 val callID: String = "",
57 val tool: String = "",
58 val state: ToolState = ToolPending,
59) : Part
61@Immutable
62@Serializable
63@SerialName("file")
64data class FilePart(
65 override val id: String = "",
66 override val messageID: String? = null,
67 override val sessionID: String? = null,
68 val mime: String? = null,
69 val filename: String? = null,
70 val url: String? = null,
71 // opencode's message-v2 schema sends `source` as a structured FilePartSource OBJECT
72 // ({ type, path, text, ... }), not a string, for @-file mentions. Typing it as String
73 // threw a SerializationException on decode — which the SSE path silently swallows, so the
74 // whole part vanished. Keep it as a tolerant JsonElement (mirroring Permission.pattern) and
75 // read the path via [sourcePath]; a bare-string source is still tolerated.
76 val source: JsonElement? = null,
77) : Part
79/** The file path referenced by a [FilePart.source]. opencode sends `source` as a FilePartSource
80 * object whose `path` field holds the file path; older/other shapes may send a bare string.
81 * Returns null when there's no usable path. */
82val FilePart.sourcePath: String?
83 get() = when (val s = source) {
84 is JsonPrimitive -> if (s.isString) s.content else null
85 is JsonObject -> (s["path"] as? JsonPrimitive)?.let { if (it.isString) it.content else null }
86 else -> null
87 }
89@Immutable
90@Serializable
91@SerialName("step-start")
92data class StepStartPart(
93 override val id: String = "",
94 override val messageID: String? = null,
95 override val sessionID: String? = null,
96) : Part
98@Immutable
99@Serializable
100@SerialName("agent")
101data class AgentPart(
102 override val id: String = "",
103 override val messageID: String? = null,
104 override val sessionID: String? = null,
105 val name: String = "",
106) : Part
108@Immutable
109@Serializable
110@SerialName("step-finish")
111data class StepFinishPart(
112 override val id: String = "",
113 override val messageID: String? = null,
114 override val sessionID: String? = null,
115 val cost: Double? = null,
116 val tokens: Tokens? = null,
117) : Part
119/** Fallback for an unrecognized `type`. */
120@Immutable
121@Serializable
122@SerialName("__unknown")
123data class UnknownPart(
124 override val id: String = "",
125 override val messageID: String? = null,
126 override val sessionID: String? = null,
127) : Part