Keep session state out of the process CWD for workspace-less requests #24
@@ -166,7 +166,15 @@ func (b *Backend) Execute(ctx context.Context, req exec.Request) (*exec.Result,
|
||||
}
|
||||
|
||||
workDir := req.WorkDir
|
||||
if !filepath.IsAbs(workDir) {
|
||||
if strings.TrimSpace(workDir) == "" {
|
||||
// 空工作区 = 请求未授权持久工作区:会话状态(.agentx/transcript 等)
|
||||
// 落一次性临时目录,不污染进程 CWD(契约套件即发此类请求)。
|
||||
tmp, err := os.MkdirTemp("", "agent-native-run-*")
|
||||
if err != nil {
|
||||
return &exec.Result{Success: false, ErrorReason: err.Error()}, nil
|
||||
}
|
||||
workDir = tmp
|
||||
} else if !filepath.IsAbs(workDir) {
|
||||
wd, _ := os.Getwd()
|
||||
workDir = filepath.Join(wd, workDir)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package native
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -65,3 +67,47 @@ func TestContractDescriptor(t *testing.T) {
|
||||
t.Fatalf("capabilities = %v", d.Capabilities)
|
||||
}
|
||||
}
|
||||
|
||||
// TestContractEmptyWorkspaceKeepsCWDClean 空 Workspace 的请求不得把会话状态
|
||||
// 写进进程 CWD(CI 护栏:make test 后工作区必须保持干净;包目录的已提交
|
||||
// .agentx 夹具内容必须逐字节不变)。
|
||||
func TestContractEmptyWorkspaceKeepsCWDClean(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
snapshot := map[string][]byte{}
|
||||
for _, rel := range []string{
|
||||
filepath.Join(".agentx", "transcript.jsonl"),
|
||||
filepath.Join(".agentx", "transcript.jsonl.meta.json"),
|
||||
} {
|
||||
if b, readErr := os.ReadFile(filepath.Join(wd, rel)); readErr == nil {
|
||||
snapshot[rel] = b
|
||||
}
|
||||
}
|
||||
b := New(Options{
|
||||
ModelOptions: ModelOptions{
|
||||
Model: "fake",
|
||||
Stream: script(llm.Message{Role: llm.RoleAssistant, Content: "hi", StopReason: llm.StopEnd}),
|
||||
},
|
||||
}).Contract()
|
||||
res, err := b.Execute(context.Background(), exec.ContractRequest{ExecutionID: "e-clean", Prompt: "hi"})
|
||||
if err != nil {
|
||||
t.Fatalf("transport error: %v", err)
|
||||
}
|
||||
if res.Outcome != exec.OutcomeSucceeded {
|
||||
t.Fatalf("outcome = %q err = %+v", res.Outcome, res.Err)
|
||||
}
|
||||
for rel, want := range snapshot {
|
||||
got, readErr := os.ReadFile(filepath.Join(wd, rel))
|
||||
if readErr != nil {
|
||||
t.Fatalf("夹具被删除:%s", rel)
|
||||
}
|
||||
if string(got) != string(want) {
|
||||
t.Fatalf("空 Workspace 的执行改写了包内夹具 %s(应落临时目录)", rel)
|
||||
}
|
||||
}
|
||||
if _, statErr := os.Stat(filepath.Join(wd, ".agentx", "activated-tools.json")); !os.IsNotExist(statErr) {
|
||||
t.Fatal("空 Workspace 的执行在包目录写下了 activated-tools.json(应落临时目录)")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user