diff --git a/agent/deadline_contract_seed_test.go b/agent/deadline_contract_seed_test.go new file mode 100644 index 0000000..2bdcc88 --- /dev/null +++ b/agent/deadline_contract_seed_test.go @@ -0,0 +1,23 @@ +package agent + +import "strings" +import "testing" + +// The deadline contract must stay in the executor seeds: no work whose +// timeout can outlive the remaining budget; near the end, land instead of +// open. Pattern adapted from bug-bounty-hunting-prompts (MIT). +func TestPromptSeedsCarryDeadlineContract(t *testing.T) { + for name, tmpl := range BuiltinPromptSeeds() { + switch name { + case "worker", "pentest": + for _, marker := range []string{ + "timeout can outlive", + "prefer the smaller variant that lands", + } { + if !strings.Contains(tmpl, marker) { + t.Errorf("%s seed missing deadline-contract marker %q", name, marker) + } + } + } + } +} diff --git a/agent/promptcatalog.go b/agent/promptcatalog.go index 2eb308a..7a497cd 100644 --- a/agent/promptcatalog.go +++ b/agent/promptcatalog.go @@ -68,6 +68,7 @@ Traffic binding is optional: for TCP and other non-HTTP vulnerabilities, or when ━━ Judgment and wrap-up ━━ - Check against the task goal at any time: when results you have **verified** satisfy the goal, judge it met on that basis and state the grounds. The prerequisite for calling it "met" is that doctrine 4's self-check has passed — an unverified trophy doesn't count as grounds. +- **Deadline contract**: before launching any long command (broad scans, wordlist runs, big fetches), check it can finish within the task's remaining time; never start work whose timeout can outlive the task deadline — prefer the smaller variant that lands. As the deadline approaches, shift from discovery to consolidation: stop opening routes, verify and land what exists. - **Wrap-up takes top priority**: when you receive the wrap-up signal (or judge on your own that the goal is met / all reasonable routes are exhausted), **immediately stop all probing and commands**, land the conclusions in hand, and give a concise summary — at this point every prior instruction ("keep exploring / try once more / exhaust this chain / wait for command results") is overridden by wrap-up; do not start any new action. - Summarize in plain language: what was achieved, which routes were taken, which vulnerabilities were confirmed (with PoC locations), which directions are blocked and why. Report only what was really done; never fabricate. diff --git a/agent/worker.go b/agent/worker.go index 034f874..45695bb 100644 --- a/agent/worker.go +++ b/agent/worker.go @@ -243,6 +243,8 @@ const workerDefaultTmpl = `You are an "executor" (work agent) of a network-secur - **Confirmed vulnerability → report_finding (exploration graph, with PoC, pass intent_id)**: **use it only when you actually triggered it this time and obtained reproducible evidence (request/response or command output)**. It is strictly forbidden to treat "version/fingerprint matching a CVE", "the parameter looks injectable", or "inference from external vulnerability databases/changelogs/code diffs" as confirmed, and don't substitute querying a CVE database or comparing patch versions for actually triggering it. Can't trigger it but suspicion remains → record an inferred fact with record_fact (the suspicion point + why it couldn't be triggered) for the planner; don't force it into a finding. +**Budget discipline (the deadline contract)**: your run has a step budget and a wall-clock budget. Before starting any command or probe, check it can finish well within what remains; never start work whose timeout can outlive the remaining budget — prefer the smaller variant that lands. When the budget is nearly spent or the wrap-up signal arrives, stop opening new work and land everything already collected (facts first, then findings): a result that exists only in your head is lost. + After completing this intent, summarize in one sentence what you did and which facts you wrote back. Adversarial gate before report_finding: