agent: dedupe transcript scanning in opencode and copilot-cli · Entire
agent: dedupe transcript scanning in opencode and copilot-cli
06f522b·
Soph·2w ago·2 files·+39 added/-75 removed
opencode's ExtractModifiedFilesFromOffset and ExtractModifiedFiles repeated the assistant-message/tool-part file collection loop — extract modifiedFilesFromMessages and call it from both. copilot-cli's summary and model extraction repeated the scan-newest-first/unmarshal/return pattern three times — extract lastEventField[T] and express the model-change-then-tool-telemetry fallback as two calls. Covered by the existing transcript extraction tests in both packages.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Sessions
11253d4586b3View transcript
Changes
2
cmd/entire/cli/agent
copilotcli
- Mtranscript.go+29/-48
- opencode
- Mtranscript.go+10/-27
168 unmodified lines
// lastEventField scans events newest-first for entries of eventType,
// returning the first non-empty value extract produces (skipping entries
// whose data doesn't unmarshal).
func lastEventField[T any](events []copilotEvent, eventType string, extract func(T) string) string {
for i := len(events) - 1; i >= 0; i-- {
if events[i].Type != eventType {
continue
}
var data T
if err := json.Unmarshal(events[i].Data, &data); err != nil {
continue
}
if v := extract(data); v != "" {
return v
}
}
return ""
}
// extractSummaryFromEvents returns the content of the last assistant.message event.
func extractSummaryFromEvents(events []copilotEvent) string {
for i := len(events) - 1; i >= 0; i-- {
if events[i].Type != eventTypeAssistantMsg {
continue
}
var data assistantMessageData
if err := json.Unmarshal(events[i].Data, &data); err != nil {
continue
}
if data.Content != "" {
return data.Content
}
}
return ""
}
// extractModelFromEvents returns the model from transcript events.
// First checks session.model_change events, then falls back to the model field
// in tool.execution_complete events (Copilot CLI includes model per tool call).
func extractModelFromEvents(events []copilotEvent) string {
// Primary: session.model_change (explicit model declaration)
for i := len(events) - 1; i >= 0; i-- {
if events[i].Type != eventTypeModelChange {
continue
}
var data modelChangeData
if err := json.Unmarshal(events[i].Data, &data); err != nil {
continue
}
if data.NewModel != "" {
return data.NewModel
}
}
if model := lastEventField(events, eventTypeModelChange,
func(d modelChangeData) string { return d.NewModel }); model != "" {
return model
}
// Fallback: tool.execution_complete events include a model field
for i := len(events) - 1; i >= 0; i-- {
if events[i].Type != eventTypeToolExecDone {
continue
}
var data toolExecCompleteData
if err := json.Unmarshal(events[i].Data, &data); err != nil {
continue
}
if data.Model != "" {
return data.Model
}
}
return ""
}
// sessionShutdownData is the data payload for session.shutdown events.
Mcmd/entire/cli/agent/copilotcli/transcript.go+29/-48
102 unmodified lines
// modifiedFilesFromMessages collects unique file paths touched by
// file-modification tool calls in msgs[startOffset:].
func modifiedFilesFromMessages(msgs []ExportMessage, startOffset int) []string {
seen := make(map[string]bool)
var files []string
for i := startOffset; i < len(session.Messages); i++ {
msg := session.Messages[i]
}
return files
}
// ExtractModifiedFiles extracts modified file paths from raw export JSON transcript bytes.