Commit Graph
586 Commits
Author SHA1 Message Date
BaaRai 4dcba3d07e Merge pull request #115 from BaaRai/fix/escalation-test-signature
fix(test): escalation test to the ctx-aware hooks.current signature
2026-10-21 15:20:16 +02:00
BaaRai a195dfee2a fix(test): escalation test to the ctx-aware hooks.current signature
Seam fix after merging #111 (escalation) with #114 (ctx-threaded
hooks.current): the escalation test predates the signature change and kept
compiling on its own branch, so the merge left main red.
2026-10-21 15:16:25 +02:00
BaaRai ff017216bf Merge pull request #114 from BaaRai/feat/per-intent-llm-rotation
feat(engine): per-intent LLM chain-head rotation (opt-in llm_rotate)
2026-10-21 15:13:56 +02:00
BaaRai 1ec2511c4b Merge pull request #113 from BaaRai/feat/php2shell-skill
feat(skill): php2shell - gated session.upload_progress LFI-to-RCE skill with vendored PoCs
2026-10-21 15:13:52 +02:00
BaaRai b757fc7e62 Merge pull request #112 from BaaRai/feat/recon-toolbox
feat(image): web-recon toolbox - nuclei, httpx, subfinder, katana, ffuf, dalfox, sqlmap
2026-10-21 15:13:47 +02:00
BaaRai 72b79af758 Merge pull request #111 from BaaRai/feat/ouroboros-takes
feat(engine): ouroboros takes - goal ambiguity gate, failover escalation, stagnation surfacing
2026-10-21 15:13:44 +02:00
BaaRai 579012e09d Merge pull request #110 from BaaRai/fix/paused-duration-freeze
fix(web): freeze session run duration while the task is paused
2026-10-21 15:13:40 +02:00
BaaRai 6894c391dd fix: test isolation for the escalation streak + compliant goal fixtures
- resetLLMFailStreaksForTest helper: the package-global streak is in-memory
  state, so the no-switch-on-ordinary-error subtests now reset it before each
  case (accumulation across subtests sharing task/profile keys made the third
  case escalate, breaking the contract the test guards)
- core lifecycle and credentials fixtures: goals now name the lab target, per
  the ambiguity gate (these tests exercise lifecycle/credentials, not the gate)
- canonical reset helper moved into task_llm.go next to the counter it resets
2026-10-21 15:08:37 +02:00
BaaRai 89348b5512 feat(engine): per-intent LLM chain-head rotation (opt-in llm_rotate)
Implements the per-intent rotation shape from #85: a task created with
llm_rotate=true distributes worker intents round-robin over its LLM chain
heads, so lane A can hunt on one model while lane B hunts on another
(different lenses per intent, no extra tokens). Default stays the classic
ordered failover from the active head; planner/goals/mainagent keep the
active head always.

Mechanics: the engine pins the next chain head into the run context when a
worker claims an intent; the pin travels through the provider resolver
(hooks.current now takes ctx). A pin is only a dispatch preference - it must
belong to the task chain and never bypasses failover state (an exhausted
chain stays exhausted). Schema: tasks.llm_rotate boolean, default false,
idempotent ALTER; exposed on the create payload and the task DTO.

Closes #85 (the ensemble/P4 shape stays parked as before)
2026-10-21 15:00:16 +02:00
BaaRai 750f4eb091 feat(skill): php2shell - gated session.upload_progress LFI-to-RCE skill with vendored PoCs
Implements #109. Skills ship the chain-1 workflow (session file injection
through upload_progress filename + app LFI -> RCE, CVE-2026-109618/109619)
with hard gates: authorized targets only, program zero-day policy checked
before any live attempt (patches landed 2026-10-21 for 8.2-8.5), passive
detection before exploitation, lab-first practice via the vendored docker
lab, chains 2/3 (UAF, serializer mismatch) marked lab-only, one-shot
verification with a marker. Upstream PoCs vendored under scripts/poc/php2shell
with attribution and the MIT notice (upstream declares MIT in its README).
2026-10-21 14:52:40 +02:00
BaaRai 2fd6ed08fc feat(image): web-recon toolbox - nuclei, httpx, subfinder, katana, ffuf, dalfox, sqlmap
Installs the drost-community take-1 subset (#100) directly into the runtime
image as pinned release binaries (plus a sqlmap checkout symlinked into
PATH), so recon-phase workers get modern tooling through their existing
Bash access with no new trust surface (no docker, no MCP wrapper). Versions
are ARGs for deliberate upgrades; each tool is executed once at build time
so a broken download fails the build instead of a hunt.

Closes #100 (takes 2 and 3 stay design notes: docker-exec integration
remains blocked per #90; catalog/execute MCP gating is future work).
2026-10-21 14:50:21 +02:00
BaaRai b237fbaa32 feat(engine): ouroboros takes - goal ambiguity gate, failover escalation, stagnation surfacing
Three orchestration mechanisms from the Q00/ouroboros evaluation (#99):

- Goal ambiguity gate at task creation: a goal must name a target (URL,
  domain, IPv4, localhost:port or absolute path) and reach a length floor,
  else the API returns 400 before any token is spent. Deliberately
  target-less goals opt out with the literal marker [targetless]. Guards
  against the live failure where a goal naming no target sent every worker
  to an unrelated asset.

- Consecutive-failure escalation on the task LLM chain: quota exhaustion
  already advances the chain; now 3 consecutive non-quota call failures on
  the same chain profile advance it too, with the true reason recorded.
  Escalation-only by design (no auto-downgrade: mid-task model swaps risk
  behavior drift; the operator keeps the manual chain reset).

- Stagnation surfacing in the planner overview: finished intents are
  fingerprinted (case-folded, digits collapsed); a direction dispatched 3+
  times with zero findings since its first attempt is flagged in a
  self-describing stagnation_warning payload that instructs the planner to
  diversify instead of grinding. Absent when nothing stagnates.

Closes #99
2026-10-21 14:46:56 +02:00
BaaRai d24e734f19 fix(web): freeze session run duration while the task is paused
Sessions keep state=running in the store during a task-level pause, so the
sessions tab treated them as live and the run duration kept ticking with
Date.now(), visibly advancing time while the task (and its deadline budget)
was frozen. Pass taskPaused down from the detail page and pin the duration
at the last recorded step while paused.

Closes #84
2026-10-21 14:34:31 +02:00
BaaRai 43a072861d Merge pull request #104 from BaaRai/rsi/shadow-lessons
feat(rsi): evidence-gated lesson library in shadow mode
2026-10-21 14:27:40 +02:00
BaaRai a8f3c2c328 feat(rsi): evidence-gated lesson library in shadow mode
Extracts candidate lessons from terminal tasks (confirmed, negative,
retest caution), gates them on cue + action + evidence, and makes
extraction idempotent through a per-source evidence key.

ARTEX_RSI_LESSONS selects off / shadow / inject. Shadow logs what would
have been shown; inject appends a code-owned, data-not-instructions block
after the planner and worker system prompts. No prompt mutation and no
adoption: status stays candidate.

Credit: the Library admission rule comes from harness2 (Google Research,
Apache-2.0, arXiv 2610.14224); the gain taxonomy from awesome-rsi
(lobehub).
2026-10-21 14:25:35 +02:00
BaaRai 61abd4522f Merge pull request #108 from BaaRai/rsi/loop-c-contract
docs(rsi): Loop C adoption contract and credits
2026-10-21 14:24:18 +02:00
BaaRai b95ece6418 Merge pull request #106 from BaaRai/rsi/skill-gaps
feat(server): capability-gap report from skill misses
2026-10-21 14:24:15 +02:00
BaaRai a98d9a1ec6 Merge pull request #102 from BaaRai/rsi/task-outcome-card
feat(server): per-task outcome card for RSI baselines
2026-10-21 14:24:10 +02:00
BaaRai 8fd83ef070 docs(rsi): Loop C adoption contract and credits
Writes down the rules any future harness-optimization work must meet:
live tasks propose and benchmarks adopt, owner approval per adoption,
immutable gates and scope checks, one-step rollback through prompt
versions, flag off by default, bounded edits, and paired attribution.

Credits harness2 (Google Research, Apache-2.0, arXiv 2610.14224) for the
Library admission rule and propose/probe/compose shape, awesome-rsi
(lobehub) for the gain taxonomy, and RRSI, ModularRSI, SEABench, Agentic
Harness Engineering and the self-evolving-agent safety survey for the
specific guardrails. No code is copied.
2026-10-19 00:33:34 +02:00
BaaRai cbb6f66c4b feat(server): capability-gap report from skill misses
GET /api/skill-gaps aggregates skill_usage rows with found=false by
skill, agent, and task category. Hits are returned for the same key so a
miss rate needs no second query; hit-only skills are excluded. The source
rows already existed: a miss is written when the model names a skill the
platform does not provide.

This is the evidence-backed capability backlog, not an auto-fixer: it
creates no skill and changes no prompt. Motivated by the harness and
skill-evolution sections of awesome-rsi (lobehub).
2026-10-19 00:33:08 +02:00
BaaRai cf369dec4f feat(server): per-task outcome card for RSI baselines
Adds GET /api/tasks/{id}/outcome: finding disposition and severity,
retest verdicts, result-row token totals, tool calls, skill misses, and
wall time in one row. This is the measurement layer an RSI loop needs
before claiming a change helped; it alters no agent behavior.

Metric selection follows harness2 (Google Research, Apache-2.0,
arXiv 2610.14224) and the task-gain / retained-gain / improver-gain
taxonomy in the awesome-rsi list.
2026-10-19 00:28:27 +02:00
BaaRai bba7989903 Merge pull request #98 from BaaRai/fix/drost-dynamic-port
fix(bench): discover the challenge port dynamically and tear down on deploy failure
2026-10-16 14:31:42 +02:00
BaaRai 89ece8eb8d fix(bench): discover the challenge port dynamically and tear down on deploy failure
The suite does not use one container port: most challenges publish 8080
but some (051, 091, 100) publish 5000, and the adapter had 8080
hardcoded, so deploy failed on those and the failure happened before
the teardown block, leaving the challenge running.

- the host port now comes from the app container port mapping (any
  container port, via docker inspect), never from an assumed 8080
- the teardown finally now wraps the deploy as well: a failure halfway
  through deploy no longer leaves containers behind (--keep and
  --deploy-only still opt out)
2026-10-16 14:29:00 +02:00
BaaRai 623e5e58a7 Merge pull request #92 from BaaRai/fix/task-detail-metrics
fix(server): fill task metrics on the detail endpoint
2026-10-16 13:38:10 +02:00
BaaRai 0777edfb8f Merge pull request #97 from BaaRai/fix/drost-finding-detail-resilience
fix(bench): survive finding-detail 404s and always tear the corpus down
2026-10-16 12:17:54 +02:00
BaaRai 67971ef7da fix(bench): survive finding-detail 404s and always tear the corpus down
The first live drost run died right after the task completed: the
findings detail route keys on finding_id (the findings-table row), not
on the exploration node id the list also carries, so every detail fetch
404ed; fetch_findings then re-raised the SystemExit from the API
wrapper, aborting scoring, the flag scan, the report and the teardown.

- details are fetched with finding_id (node id as fallback) and a
  missing detail no longer kills the run: the list fields remain and the
  finding stays gradeable
- the stored finding carries finding_id so the deep flag scan can
  re-fetch untruncated text
- the deep scan tolerates the same failures
- teardown runs in a finally around the whole task phase, so a crash in
  scoring or reporting can no longer leave the challenge running
  (--keep still opts out deliberately)
2026-10-16 12:15:02 +02:00
BaaRai 57cac93d49 Merge pull request #96 from BaaRai/fix/drostbench-goal-target
fix(bench): name the target URL in the DrostBench task goal
2026-10-16 12:04:46 +02:00
BaaRai 58b221bfe1 fix(bench): name the target URL in the DrostBench task goal
The drost task goal template never included the target URL, so the
created task named no target: the first live run's planner matched the
challenge description against the global asset store and sent every
worker to an unrelated WordPress lab from past tasks. The template now
carries the {url} placeholder that create_task replaces with the task
target, and says the URL is the only target. The unit test locks the
placeholder in.
2026-10-16 12:02:01 +02:00
BaaRai 5d72de4ef7 Merge pull request #95 from BaaRai/feat/drostbench-flag-corpus
feat(bench): add the DrostBench corpus with a flag oracle
2026-10-16 11:47:45 +02:00
BaaRai 19e929c224 feat(bench): let runs pick the task llm chain and host-reachable target
--llm-profiles passes llm_profile_ids to the created task (head first)
so a benchmark run exercises the model chain it claims to measure, and
the chain is stamped into results.json and the report.

--target-host rewrites the host in the task goal and company scope when
ARTEX runs in a container and the corpus runs on the host: the task must
reach the challenge via the host gateway (172.17.0.1), the same pattern
as the MCP server URLs. Local reachability checks still use 127.0.0.1.
2026-10-16 11:45:00 +02:00
BaaRai d2f4697b2b feat(bench): add the DrostBench corpus with a flag oracle
DrostBench v1 ships 100 docker-compose web challenges, each with one
flag-bearing vulnerability plus decoys that do not reach the flag, so
flag capture is an objective win condition that no judge decides.

scripts/benchmark/drost.py drives one challenge: compose build with an
explicit per-build random flag (the suite's deterministic sha256 default
is never used, so a memorized flag cannot be replayed), up --wait, host
port discovery, down --rmi local. Ground truth is derived from the
challenge benchmark.json; the flag is scanned over the fetched findings
first and over full details, facts and the report only before declaring
a miss.

run_bench.py gains --corpus-type drost (one challenge per invocation),
flag capture in every drost report, a flag-only mode when no judge is
configured (gates reported as not measured, never as zero), and
--deploy-only for install validation. Image mode is unchanged.

test_drost.py covers the pure functions (flag formats, challenge id
validation, port parsing, ground-truth derivation incl. its refusals);
the lifecycle was exercised end to end on DBEN-v1-001: build with a
random flag, reachable app, compiled flag matching the override,
teardown without leftovers.

Closes #94
2026-10-16 11:27:54 +02:00
BaaRai b269d3e590 fix(server): fill task metrics on the detail endpoint
GET /api/tasks/{id} returned the metrics block zeroed (tokens, goal
progress, in_flight, findings, last_activity) because only listTasks
populated those DTO fields. Extract the fill into applyTaskMetrics and
call it from both handlers so the two endpoints report identical
numbers from the same TaskListMetricsAll query. The swallowed query
error in listTasks is now logged instead of silently zeroing the table.

Test: TestTaskDetailReturnsListMetrics seeds result-activity tokens and
goal nodes, asserts the detail endpoint reports them and matches the
list endpoint; fails on main, passes with the fix.

Fixes #91
2026-10-13 14:49:47 +02:00
BaaRai 908832ae1b Merge pull request #88 from BaaRai/feat/planner-quiet-mode-toggle
feat: per-task planner quiet-mode switch, editable mid-run
2026-10-12 21:13:40 +02:00
BaaRai 9f03b8c16a feat: per-task planner quiet-mode switch, editable mid-run
Quiet-mode was compile-time only; wanting it off mid-task (empty frontier, one long intent, idle lanes) was impossible. tasks.plan_quiet_mode (default on) gates the edge-round skip in runRound, PATCH /api/tasks/{id} flips it live (the next round reads it), creation accepts plan_quiet_mode, and the DTO exposes it.
2026-10-12 21:13:28 +02:00
BaaRai 8d841a6011 Merge pull request #83 from BaaRai/fix/inheritance-test-teardown-order
fix: inheritance test teardown closes the manager after the DeleteTask cleanups
2026-10-12 18:46:55 +02:00
BaaRai 35bd98561c Merge pull request #82 from BaaRai/feat/test-db-per-package
feat: each test package runs on its own database
2026-10-12 18:46:51 +02:00
BaaRai d0ffd96960 Merge pull request #81 from BaaRai/feat/planner-quiet-mode
feat: planner quiet-mode skips edge rounds while work runs and goals hold
2026-10-12 18:46:48 +02:00
BaaRai ac8491d666 fix: inheritance test teardown closes the manager after the DeleteTask cleanups
defer m.Close() ran before the t.Cleanup DeleteTask calls, so they raced a closed database and the late writers they trigger could still touch the temp dir while RemoveAll was already walking it (the CI-only TempDir: directory not empty flake). The manager now closes via a t.Cleanup registered first (runs last, LIFO) and the temp dir is removed with a tolerated failure.
2026-10-12 18:43:41 +02:00
BaaRai aaf966872a feat: each test package runs on its own database
agent, db, server and evidence serialized on one shared test database via an advisory lock, while evidence, llmrec and traffic-style packages could still run against it in parallel; under full-suite load that window produced a real SQLSTATE 40P01 deadlock in the blackboard direct-source test. TestMain in every PG-using package now rewrites ARTEX_PG_DSN to a per-package database (created automatically by ensureDatabase), so packages neither contend nor need to serialize; the advisory lock stays as an in-package safety net. llmrec gains the TestMain it lacked, which also gives its requires-isolated-DSN tests a truly isolated database inside ./... runs.
2026-10-12 18:43:29 +02:00
BaaRai f57cf659b5 feat: planner quiet-mode skips edge rounds while work runs and goals hold
Every worker write-back fired a full planning round even when the open-goal set had not changed; on a live three-worker task that was 7 rounds in 45 minutes. Edge-triggered rounds are now dropped while active intents exist and the open-goal fingerprint recorded at the end of the last round is unchanged; heartbeat rounds, goal-set changes and frontier drains still plan immediately. Fails open on store errors.
2026-10-12 18:36:44 +02:00
BaaRai ff2e0bf338 Merge pull request #77 from BaaRai/feat/skills-reverse-skill-ports
feat(skills): port patch-diff, api-security, edr-bypass-re from reverse-skill
2026-10-12 16:34:48 +02:00
BaaRai 53bda9fe37 feat(skills): port edr-bypass-re playbook from reverse-skill (MIT)
Defense-telemetry analysis for authorized adversary simulation: EDR
monitoring surfaces, hook survey workflow, countermeasure selection,
MITRE T1562 mapping. Lab-evaluation framing with strict authorization
boundaries; requires an authorized Windows lab VM.

Adapted from zhaoxuya520/reverse-skill skills/edr-bypass-re (MIT).
Refs #76
2026-10-12 13:15:05 +02:00
BaaRai a11c2ed10c feat(skills): port api-security playbook from reverse-skill (MIT)
Ten-phase authorized API hunting methodology (BOLA/IDOR/BFLA, JWT and
OAuth, GraphQL abuse, input validation, logic, rate-limit trust
confusion, data exposure) as the hunting counterpart of api-recon.

Adapted from zhaoxuya520/reverse-skill skills/api-security (MIT).
Refs #76
2026-10-12 13:15:05 +02:00
BaaRai 04e8b37368 feat(skills): port patch-diff playbook from reverse-skill (MIT)
N-day recovery from vendor fixes: diff before/after builds, infer the
bug class from added guards, verify with a PoC that crashes only the
unpatched build. Adapted to platform rules: authorization boundaries,
standing quality gates, no simulated results, rea MCP notes.

Adapted from zhaoxuya520/reverse-skill skills/patch-diff-exploit (MIT).
Refs #76
2026-10-12 13:15:05 +02:00
BaaRai 2cdedd8eee Merge pull request #75 from BaaRai/fix/llm-test-stored-profile-endpoint
fix: llm/test stored profile fills endpoint fields from the row
2026-10-12 08:55:32 +02:00
BaaRai 2f80ffd979 Merge pull request #74 from BaaRai/fix/cost-ledger-fresh-input
fix: cost ledger charges fresh input only (cache read is part of input tokens)
2026-10-12 08:55:29 +02:00
BaaRai 6b1f04b1ba fix: llm/test stored profile fills endpoint fields from the row
The fallback only borrowed the stored key; provider, model, base_url, proxy, streaming and thinking settings still came from the request form. An empty form (the list-view test button) then hit ConfigFrom defaults, sending the profile key to the canonical provider endpoint and 401ing. Fields the form leaves empty now come from the stored profile; explicit form values still win.
2026-10-12 08:54:29 +02:00
BaaRai c96c3ea3a9 fix: cost ledger charges fresh input only (cache read is part of input tokens)
input_tokens includes the cached subset under Anthropic and OpenAI usage semantics, so pricing input and cache read at their full prices double-counted the cache. The fresh share is now input minus cache reads, clamped at zero for gateways reporting disjoint buckets.
2026-10-12 08:53:20 +02:00
BaaRai e36d0730da Merge pull request #71 from BaaRai/feat/cost-ledger
feat: cost ledger (per-profile EUR pricing on token usage)
2026-10-12 08:25:41 +02:00
BaaRai a183c4aad5 Merge pull request #69 from BaaRai/feat/submission-review
feat: pre-submission report review (validate against program rules)
2026-10-12 08:25:38 +02:00