MCPcopy Create free account
hub / github.com/atomicdotdev/atomic / assemble

Function assemble

atomic-agent/src/transcript/opencode.rs:236–384  ·  view source on GitHub ↗

Fold messages and parts into [`TurnData`]. The current turn starts at the last user message; reasoning, response, and step statistics are scoped to that window, while the transcript covers the whole session (mirroring how Claude Code's transcript file is a full-session record).

(messages: &[(String, String)], parts: &[(String, Value)])

Source from the content-addressed store, hash-verified

234/// covers the whole session (mirroring how Claude Code's transcript file
235/// is a full-session record).
236fn assemble(messages: &[(String, String)], parts: &[(String, Value)]) -> TurnData {
237 let role_of: HashMap<&str, &str> = messages
238 .iter()
239 .map(|(id, role)| (id.as_str(), role.as_str()))
240 .collect();
241
242 // Turn window: message indices from the last user message onward.
243 let turn_start = messages
244 .iter()
245 .rposition(|(_, role)| role == "user")
246 .unwrap_or(0);
247 let in_turn: HashMap<&str, bool> = messages
248 .iter()
249 .enumerate()
250 .map(|(i, (id, _))| (id.as_str(), i >= turn_start))
251 .collect();
252
253 let mut data = TurnData {
254 transcript_jsonl: String::new(),
255 reasoning_blocks: Vec::new(),
256 response: None,
257 input_tokens: 0,
258 output_tokens: 0,
259 reasoning_tokens: 0,
260 cache_read_tokens: 0,
261 cache_write_tokens: 0,
262 cost_usd: 0.0,
263 finish_reason: None,
264 step_count: 0,
265 tool_parts: Vec::new(),
266 };
267
268 let push_line = |line: Value, out: &mut TurnData| {
269 if let Ok(s) = serde_json::to_string(&line) {
270 out.transcript_jsonl.push_str(&s);
271 out.transcript_jsonl.push('\n');
272 }
273 };
274
275 for (message_id, part) in parts {
276 let part_type = part.get("type").and_then(Value::as_str).unwrap_or("");
277 let in_turn = in_turn.get(message_id.as_str()).copied().unwrap_or(false);
278 let role = role_of
279 .get(message_id.as_str())
280 .copied()
281 .unwrap_or("assistant");
282
283 match part_type {
284 "text" => {
285 let Some(text) = part.get("text").and_then(Value::as_str) else {
286 continue;
287 };
288 if text.trim().is_empty() {
289 continue;
290 }
291 let line = serde_json::json!({ "type": role, "text": text });
292 push_line(line, &mut data);
293 if in_turn && role == "assistant" {

Calls 5

getMethod · 0.65
iterMethod · 0.45
as_strMethod · 0.45
pushMethod · 0.45
is_emptyMethod · 0.45