import: address review — pi branch-safe model, copilot streaming scan · Entire

import: address review — pi branch-safe model, copilot streaming scan

be12306→main·

computermode·3w ago·3 files·+44 added/-6 removed

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

Sessions

0b1a3d9f7f74View transcript

Changes

3

1  
2  
3  
4  
5  
6  
7  
66 unmodified lines

74  
75  
76  
76  
77  
78  
79  
80  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
3 unmodified lines

94  
95  
96  
90  
97  
98  
99  
100  
  
package agentimport

import (  
    "bufio"  
    "context"  
    "encoding/json"  
    "fmt"  
66 unmodified lines  
// it in this repo (gitRoot or cwd is the repo root or a descendant). Sessions  
// whose location can't be determined are treated as not belonging to the repo.  
func copilotSessionInRepo(path, repoRoot string) bool {  
    data, err := os.ReadFile(path) //nolint:gosec // path discovered under the configured session dir  
    f, err := os.Open(path) //nolint:gosec // path discovered under the configured session dir  
    if err != nil {  
        return false  
    }  
    for _, raw := range splitRawLines(data) {  
    defer func() { _ = f.Close() }()

// Scan line-by-line and stop at the first session.start (normally line 0)  
    // rather than slurping the whole transcript, which can be large.  
    scanner := bufio.NewScanner(f)  
    scanner.Buffer(make([]byte, 0, bufio.MaxScanTokenSize), 10*1024*1024) // 10 MB max line  
    for scanner.Scan() {  
        var evt struct {  
            Type string `json:"type"`  
            Data struct {  
3 unmodified lines

} `json:"context"`  
        } `json:"data"`  
    }  
        if err := json.Unmarshal(raw, &evt); err != nil || evt.Type != "session.start" {  
        if err := json.Unmarshal(scanner.Bytes(), &evt); err != nil || evt.Type != "session.start" {  
            continue  
        }  
        return repoMatches(evt.Data.Context.GitRoot, repoRoot) || repoMatches(evt.Data.Context.Cwd, repoRoot)  

Mcmd/entire/cli/agentimport/copilot.go+10/-3

93 unmodified lines

94  
95  
96  
97  
98  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
104  
109  
110  
111  
112

93 unmodified lines

end = starts[k+1]  
    }

// Bound token usage to [start, end): truncate to the first `end` lines,  
        // then let the agent helper slice from `start`.  
        // Bound this turn to [start, end) by truncating to the first `end` lines  
        // (keeping the file from line 0). Pi's branch-aware helpers resolve the  
        // active branch by walking parentId back to the root, so the prefix MUST  
        // retain the beginning — truncating the end is safe (parents are always  
        // earlier lines) but slicing off the start would break those chains.  
        // CalculateTokenUsage then slices forward from `start`; ExtractModel  
        // reports the active-branch model as of this turn's end.  
        truncated := joinLines(rawLines[:end])  
        tokens, err := ag.CalculateTokenUsage(truncated, start)  
        if err != nil {  
            return nil, fmt.Errorf("token usage for turn %d: %w", k, err)  
        }  
        model, mErr := ag.ExtractModel(joinLines(rawLines[start:end]))  
        model, mErr := ag.ExtractModel(truncated)  
        if mErr != nil {  
            model = ""  
        }  

Mcmd/entire/cli/agentimport/pi.go+8/-3

79 unmodified lines

80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111

79 unmodified lines

}  
}  
// TestPiSplitTurns_ModelInheritedOverPrefix guards the branch-resolution fix:  
// a later turn whose own assistant message omits the model must still resolve  
// the model from an earlier active-branch message. This only works when the  
// model is extracted over the [0,end) prefix (chains intact); extracting over  
// the [start,end) slice would strip the earlier model and yield "".  
func TestPiSplitTurns_ModelInheritedOverPrefix(t *testing.T) {  
    t.Parallel()  
    full := []byte(strings.Join([]string{  
        `{"type":"message","id":"pu1","message":{"role":"user","content":"first"}}`,  
        `{"type":"message","id":"pa1","message":{"role":"assistant","content":[{"type":"text","text":"ok"}],"model":"model-A","usage":{"input":10,"output":5}}}`,  
        `{"type":"message","id":"pu2","message":{"role":"user","content":"second"}}`,  
        `{"type":"message","id":"pa2","message":{"role":"assistant","content":[{"type":"text","text":"done"}],"usage":{"input":20,"output":7}}}`,  
    }, "\n") + "\n")

turns, err := piImporter{}.SplitTurns(SessionFile{Path: filepath.Join(t.TempDir(), "s.jsonl"), SessionID: "s"}, full)  
    if err != nil {  
        t.Fatal(err)  
    }  
    if len(turns) != 2 {  
        t.Fatalf("want 2 turns, got %d", len(turns))  
    }  
    if turns[1].Model != "model-A" {  
        t.Errorf("turn1 model = %q, want model-A inherited from the active branch", turns[1].Model)  
    }  
}

func TestPiSplitTurns_ToolResultIsNotATurn(t *testing.T) {  
    t.Parallel()  
    // A toolResult-role message is not a user prompt and must not start a turn.  

Mcmd/entire/cli/agentimport/pi_test.go+26