| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "strings" |
| 6 | |
| 7 | "reasonix/internal/agent" |
| 8 | "reasonix/internal/provider" |
| 9 | ) |
| 10 | |
| 11 | // A generation is one round of work followed by one compaction. Compaction |
| 12 | // re-derives its digest from the whole canonical transcript, so generation N |
| 13 | // folds everything generations 1..N produced — which is exactly the growth the |
| 14 | // cost arm measures and the drift arm probes. |
| 15 | const ( |
| 16 | unitsPerGeneration = 12 |
| 17 | toolResultBytes = 6_000 |
| 18 | ) |
| 19 | |
| 20 | // workUnit is one read_file round: the shape that dominates a real coding |
| 21 | // session's token weight. |
| 22 | func workUnit(sess *agent.Session, gen, i int) { |
| 23 | id := fmt.Sprintf("g%02dc%02d", gen, i) |
| 24 | path := fmt.Sprintf("internal/pkg%02d/file%02d.go", gen, i) |
| 25 | sess.Add(provider.Message{Role: provider.RoleAssistant, ToolCalls: []provider.ToolCall{ |
| 26 | {ID: id, Name: "read_file", Arguments: fmt.Sprintf(`{"path":%q}`, path)}, |
| 27 | }}) |
| 28 | sess.Add(provider.Message{Role: provider.RoleTool, ToolCallID: id, Name: "read_file", Content: fakeGoFile(gen, i, toolResultBytes)}) |
| 29 | sess.Add(provider.Message{Role: provider.RoleAssistant, Content: fmt.Sprintf("Read %s; nothing surprising.", path)}) |
| 30 | } |
| 31 | |
| 32 | func fakeGoFile(gen, i, size int) string { |
| 33 | var b strings.Builder |
| 34 | fmt.Fprintf(&b, "package pkg%02d\n\n// file%02d\n\n", gen, i) |
| 35 | for line := 0; b.Len() < size; line++ { |
| 36 | fmt.Fprintf(&b, "func helper%02d_%02d_%04d(x int) int { return x*%d + %d }\n", gen, i, line, line+3, line*7) |
| 37 | } |
| 38 | return b.String() |
| 39 | } |
| 40 | |
| 41 | // growSession appends one generation of work, planting whatever probes belong |
| 42 | // to that generation so a fact's age is exactly the number of folds it has |
| 43 | // survived. |
| 44 | func growSession(sess *agent.Session, gen int, probes []probe) { |
| 45 | for _, p := range probes { |
| 46 | if p.plantAt == gen { |
| 47 | p.plant(sess) |
| 48 | } |
| 49 | if revise, ok := p.later[gen]; ok { |
| 50 | revise(sess) |
| 51 | } |
| 52 | } |
| 53 | for i := range unitsPerGeneration { |
| 54 | workUnit(sess, gen, i) |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | func newSession() *agent.Session { |
| 59 | sess := agent.NewSession("You are a terse coding agent. Answer questions from what you know; do not guess.") |
| 60 | sess.Add(provider.Message{Role: provider.RoleUser, Content: "Fix the config round-trip formatting bug in this repo."}) |
| 61 | return sess |
| 62 | } |
| 63 | |
| 64 | // visibleContext splices the stored projection with the canonical messages |
| 65 | // appended after it — the same model-visible view the agent would send. |
| 66 | func visibleContext(sessionPath string, sess *agent.Session) ([]provider.Message, error) { |
| 67 | st, ok, err := agent.LoadCompactionState(sessionPath) |
| 68 | if err != nil { |
| 69 | return nil, err |
| 70 | } |
| 71 | canonical := sess.Snapshot() |
| 72 | if !ok || len(st.Projection.Messages) == 0 { |
| 73 | return canonical, nil |
| 74 | } |
| 75 | out := append([]provider.Message(nil), st.Projection.Messages...) |
| 76 | if n := st.Projection.CoveredCount; n >= 0 && n < len(canonical) { |
| 77 | out = append(out, canonical[n:]...) |
| 78 | } |
| 79 | return out, nil |
| 80 | } |
| 81 |