Files
ARTEX/server/ask_operator_test.go
T
BaaRai 4b815d5ee0 feat: ask_operator environmental handoff discipline
When a worker hits an environmental failure (sandboxed or filtered
network path, missing binary or key, TLS or proxy interception, missing
runtime permission), nothing today tells it how to stop: transcripts
show turn after turn of retries with different flags, equivalent tools,
or fallback transports, all burning budget against a wall the agent
cannot move.

Changes:

- the ask_operator tool description now covers environmental blocks
  explicitly: do not retry or work around; include the exact single
  command the operator should run in their own terminal as one
  copy-pastable line, plus what to paste back, then wait. The question
  parameter description gains the same guidance.
- worker and pentest seeds gain an "Operator handoff on environmental
  blocks" block: stop after the first environmental failure, no
  flag-hopping, no bypass hunting, one command per ask_operator request,
  never batch.
- tests: the tool description must keep the handoff markers
  (TestAskOperatorEnvHandoffGuidance) and the executor seeds must keep
  the block (TestPromptSeedsCarryOperatorHandoff).

Pattern adapted from bug-bounty-hunting-prompts (MIT), whose operator
handoff rule produced exactly this single-command format.
2026-10-12 06:45:21 +02:00

83 lines
2.9 KiB
Go

package server
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
func TestStageAskOperatorImage(t *testing.T) {
dir := t.TempDir()
src := filepath.Join(dir, "shot.png")
if err := os.WriteFile(src, []byte("fakepng"), 0o644); err != nil {
t.Fatal(err)
}
// no image -> no attachment
att, err := stageAskOperatorImage(dir, "7", "")
if err != nil || att != nil {
t.Fatalf("empty src: att=%v err=%v", att, err)
}
// valid image -> copied under the task uploads, descriptor matches the chat format
att, err = stageAskOperatorImage(dir, "7", src)
if err != nil || len(att) != 1 {
t.Fatalf("valid src: att=%v err=%v", att, err)
}
if !strings.HasPrefix(att[0].Path, "uploads/ask-operator-") || !strings.HasSuffix(att[0].Path, ".png") {
t.Fatalf("unexpected attachment path %q", att[0].Path)
}
if _, err := os.Stat(filepath.Join(dir, "tasks", "7", "uploads", att[0].Name)); err != nil {
t.Fatalf("copy missing: %v", err)
}
// wrong extension rejected
bad := filepath.Join(dir, "payload.exe")
if err := os.WriteFile(bad, []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
if _, err := stageAskOperatorImage(dir, "7", bad); err == nil || !strings.Contains(err.Error(), "png/jpg") {
t.Fatalf("expected extension rejection, got %v", err)
}
// missing file rejected
if _, err := stageAskOperatorImage(dir, "7", filepath.Join(dir, "nope.png")); err == nil {
t.Fatal("expected missing-file rejection")
}
}
// The emitted activity must carry the {text, attachments} Detail shape the
// transcript parser expects, so the operator sees the question with the
// attachment card for the screenshot.
func TestAskOperatorActivityShape(t *testing.T) {
att := []chatAttachment{{Name: "ask-operator-1.png", Path: "uploads/ask-operator-1.png", Size: 7}}
a := userActivityWithAttachments("worker", "OPERATOR INPUT REQUESTED: solve the captcha", att)
if a.Kind != "user" || a.Worker != "worker" {
t.Fatalf("unexpected activity kind/worker: %+v", a)
}
var parsed struct {
Text string `json:"text"`
Attachments []chatAttachment `json:"attachments"`
}
if err := json.Unmarshal([]byte(a.Detail), &parsed); err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(parsed.Text, "OPERATOR INPUT REQUESTED") || len(parsed.Attachments) != 1 || parsed.Attachments[0].Path != att[0].Path {
t.Fatalf("unexpected detail payload: %+v", parsed)
}
}
// The tool description must tell agents what to do on environmental blocks:
// hand the operator one copy-pastable command instead of retrying or hunting
// for bypasses. Pattern adapted from bug-bounty-hunting-prompts (MIT).
func TestAskOperatorEnvHandoffGuidance(t *testing.T) {
desc := (&Server{}).toolAskOperator().Description()
for _, marker := range []string{"copy-pastable", "do not retry or work around"} {
if !strings.Contains(desc, marker) {
t.Errorf("ask_operator description missing env-handoff marker %q", marker)
}
}
}