mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
A turn that ends with its answer is done. The agent no longer asks, at the point of stopping, whether a check ran after the last write, whether a high-risk change was reviewed, or whether captured test criteria still hold; it no longer ends a run with a readiness error, and the controller no longer re-runs the turn to satisfy one. Goal keeps its update_goal report and the evaluator, and still holds a complete claim while the task list is unfinished; it no longer reads a readiness result or freezes a verification contract. Review sub-agents are ordinary sub-agents: nothing requires them to file a typed report. Removes the readiness, owed-review, baseline-criteria, carried-proof and review-report code, the delivery checkpoint, the readiness continuation, the orphaned helpers they left in evidence, and the tests that asserted the removed behaviour.
106 lines
3.3 KiB
Go
106 lines
3.3 KiB
Go
package agent
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
|
|
"reasonix/internal/base/hostwrite"
|
|
"reasonix/internal/runtime/writeclaim"
|
|
"reasonix/internal/state/sessiontemp"
|
|
)
|
|
|
|
// outsideObservedWrites names the files this turn wrote where nothing the host
|
|
// watches can speak for them. A write there is neither a probe nor the work
|
|
// product: the host saw the call succeed and can say nothing about what it left.
|
|
func (a *Agent) outsideObservedWrites() []string {
|
|
if a == nil || a.task.ledger == nil || a.writeWorkspaceRoot == "" {
|
|
return nil
|
|
}
|
|
var outside []string
|
|
for _, r := range a.task.ledger.Receipts() {
|
|
if !r.Success || !(r.Write || r.Mutation) {
|
|
continue
|
|
}
|
|
for _, p := range r.Paths {
|
|
if p == "" || slices.Contains(r.HostOwned, p) || a.pathInWorkspace(p) || underScratchOrCache(p) {
|
|
continue
|
|
}
|
|
if !slices.Contains(outside, p) {
|
|
outside = append(outside, p)
|
|
}
|
|
}
|
|
}
|
|
return a.relativeUnique(outside)
|
|
}
|
|
|
|
// underScratchOrCache reports a path the host disposes of on its own: inside the
|
|
// directory this session was handed for temporary work, or inside a toolchain
|
|
// cache. A shared temporary root is neither — a person who asked for a file
|
|
// there asked for something to keep, and nothing the host watches says it
|
|
// arrived.
|
|
func underScratchOrCache(path string) bool {
|
|
return disposable(path, os.TempDir(), hostwrite.Caches())
|
|
}
|
|
|
|
// disposable is the rule above with the host's roots passed in, so its edges are
|
|
// decidable without an environment that happens to sit where the rule looks.
|
|
func disposable(path, tempRoot string, caches []string) bool {
|
|
if !filepath.IsAbs(path) {
|
|
return false
|
|
}
|
|
target := filepath.Clean(path)
|
|
if insideSessionScratch(target, tempRoot) {
|
|
return true
|
|
}
|
|
for _, dir := range caches {
|
|
dir = strings.TrimSpace(dir)
|
|
if dir == "" {
|
|
continue
|
|
}
|
|
clean := filepath.Clean(dir)
|
|
if !writeclaim.PathWithin(clean, target) {
|
|
continue
|
|
}
|
|
// An executable installed under a cache is the user's, not the cache's:
|
|
// `go install` lands in ~/go/bin, which is what the turn was asked for.
|
|
if rel, err := filepath.Rel(clean, target); err == nil && strings.HasPrefix(filepath.ToSlash(rel), "bin/") {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// insideSessionScratch reports a path under a directory sessiontemp creates:
|
|
// directly beneath the temporary root, named with the scratch prefix. Only the
|
|
// first component below that root is read, so a folder deeper in the tree cannot
|
|
// earn an exemption by spelling the prefix.
|
|
func insideSessionScratch(path, tempRoot string) bool {
|
|
if tempRoot == "" {
|
|
return false
|
|
}
|
|
rel, err := filepath.Rel(filepath.Clean(tempRoot), path)
|
|
if err != nil || rel == "." || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
|
return false
|
|
}
|
|
first, _, _ := strings.Cut(rel, string(filepath.Separator))
|
|
return strings.HasPrefix(first, sessiontemp.Prefix())
|
|
}
|
|
|
|
func (a *Agent) relativeUnique(paths []string) []string {
|
|
out := make([]string, 0, len(paths))
|
|
for _, p := range paths {
|
|
if filepath.IsAbs(p) && a.observeRoot != "" {
|
|
if rel, err := filepath.Rel(a.observeRoot, p); err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
|
p = rel
|
|
}
|
|
}
|
|
if !slices.Contains(out, p) {
|
|
out = append(out, p)
|
|
}
|
|
}
|
|
return out
|
|
}
|