Skip to content

feat: support local-script targets in opfor hunt#192

Open
jithin23-kv wants to merge 2 commits into
KeyValueSoftwareSystems:masterfrom
jithin23-kv:feat/hunt-local-script-target
Open

feat: support local-script targets in opfor hunt#192
jithin23-kv wants to merge 2 commits into
KeyValueSoftwareSystems:masterfrom
jithin23-kv:feat/hunt-local-script-target

Conversation

@jithin23-kv

@jithin23-kv jithin23-kv commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Problem

opfor hunt (the autonomous commander/operator/scout red-teaming mode) only supported HTTP targets. If a target's chat API couldn't be modeled as a simple request/response HTTP call — e.g. async/polling APIs, session ids embedded in a URL path segment rather than a body/header field (which session.send has no way to express), or auth flows needing custom logic — there was no way to point opfor hunt at it. opfor run already supported type: "local-script" targets for exactly this case; opfor hunt explicitly threw "local-script targets are not supported by opfor hunt (HTTP only).".

Separately, --ui had a bug: it fell back to launching the setup wizard whenever --endpoint was omitted, even when a valid target was already supplied via --target-config (which is how local-script targets are configured, since they have no endpoint).

Solution

  • Added a TargetKind ("http" | "local-script") to the autonomous runner's TargetConfig, plus a scriptPath field.
  • Added createLocalScriptTargetClient(), a TargetClient implementation that shells out to a user script per turn (via the same invokeLocalTargetScript stdin/stdout contract opfor run already uses) instead of making an HTTP request. Hunt's per-thread threadId is passed through as the script's sessionId, so thread forks each get isolated sessions automatically — no extra wiring needed.
  • createTargetClient() now branches on config.type to pick the HTTP or local-script client.
  • Removed the hard throw in mapAgentTargetToAutonomous() and added proper mapping, validation (checks scriptPath instead of endpoint), and name-fallback (script basename instead of URL host) for local-script targets.
  • Fixed --ui's fallback condition to also check !opts.targetConfig, so it only launches the setup wizard when no target was specified at all.
  • Bumped the local-script kill timeout from 30s to 240s (slow/polling-based target scripts were getting killed mid-turn).
  • Fixed local-script error responses not being prefixed with "ERROR:", which meant isTargetError() never actually flagged them as errors (it checks response.startsWith("ERROR:")) — errors from a script were silently treated as normal target replies.

Changes

  • core/src/autonomous/lib/types.tsTargetKind type, type/scriptPath fields on TargetConfig
  • core/src/autonomous/target/http.ts — branch to createLocalScriptTargetClient() in createTargetClient()
  • core/src/autonomous/target/localScript.ts (new) — local-script TargetClient implementation
  • core/src/lib/localScriptTarget.ts — timeout 30s → 240s; error responses now prefixed ERROR:
  • runners/cli/src/commands/hunt.ts — local-script mapping/validation/naming in mapAgentTargetToAutonomous(); --ui fallback condition fix

Issue

N/A

Summary by CodeRabbit

  • New Features

    • Added local-script target support to opfor hunt, configurable via --target-config (including session, prompt, model, and response settings).
    • Local-script targets can run in stateful or stateless mode.
    • When --ui is used, the CLI now enters setup mode only if no target is already configured; otherwise it opens the live dashboard for the configured target.
  • Bug Fixes

    • Improved validation and default naming for local-script targets.
    • Increased local-script execution deadline to 240s.
    • Local-script errors now include an ERROR: prefix for clarity.
  • Documentation

    • Updated hunt guidance, adding a Local-script targets section and example configurations.

Extends `opfor hunt` to accept `type: "local-script"` targets (previously
HTTP-only), matching the local-script support already in `opfor run`. Adds
a TargetClient that shells out to a user script per turn instead of making
an HTTP request, threading hunt's per-thread threadId through as the
script's sessionId so thread forks get proper session isolation for free.

Also fixes two related bugs:
- local-script error responses weren't prefixed with "ERROR:", so
  isTargetError() never flagged them as target errors
- --ui fell back to the setup wizard whenever --endpoint was absent,
  even when --target-config supplied a valid local-script target
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds local-script targets to autonomous target configuration and client creation. The hunt CLI maps and validates these targets, while local script invocation gains a longer timeout and prefixed error messages.

Changes

Local-script target support

Layer / File(s) Summary
Target configuration and client routing
core/src/autonomous/lib/types.ts, core/src/autonomous/target/http.ts
Adds TargetKind, local-script configuration fields, and routing from createTargetClient to the local-script client.
Local-script invocation and result mapping
core/src/autonomous/target/localScript.ts, core/src/lib/localScriptTarget.ts
Adds script execution through invokeLocalTargetScript, maps responses and errors, extends the timeout, and prefixes parsed script errors.
Hunt CLI target handling and documentation
runners/cli/src/commands/hunt.ts, docs/hunt.md
Maps and validates local-script targets, adjusts UI setup behavior and target naming, and documents local-script configuration and session handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HuntCLI
  participant TargetClientFactory
  participant LocalScriptTargetClient
  participant LocalScriptInvoker
  HuntCLI->>TargetClientFactory: Create client from local-script TargetConfig
  TargetClientFactory->>LocalScriptTargetClient: Route local-script target
  LocalScriptTargetClient->>LocalScriptInvoker: Invoke script with prompt and thread session
  LocalScriptInvoker-->>LocalScriptTargetClient: Return response or error
  LocalScriptTargetClient-->>HuntCLI: Return mapped target result
Loading

Suggested reviewers: achuvyas-kv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding local-script target support to opfor hunt.
Description check ✅ Passed The description follows the template closely and covers the problem, solution, changes, and issue; only the testing and screenshots sections are missing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core/src/autonomous/target/localScript.ts (1)

12-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the error message actionable.

Per coding guidelines, error messages should tell the user how to fix the problem, not just that something failed. The current message says what's missing but not how to set it.

As per coding guidelines: "Write actionable error messages that tell the user how to fix the problem, not just that something failed."

♻️ Proposed fix
-    throw new Error("local-script target is missing `scriptPath`.");
+    throw new Error(
+      "local-script target is missing `scriptPath`. Set `scriptPath` on the target in --target-config (e.g., { \"type\": \"local-script\", \"scriptPath\": \"./adapter.js\" })."
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/src/autonomous/target/localScript.ts` around lines 12 - 15, Update the
missing-scriptPath error in createLocalScriptTargetClient to include actionable
guidance explaining that the caller must set config.scriptPath to the local
script path, while preserving the existing validation behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@core/src/autonomous/target/localScript.ts`:
- Around line 12-15: Update the missing-scriptPath error in
createLocalScriptTargetClient to include actionable guidance explaining that the
caller must set config.scriptPath to the local script path, while preserving the
existing validation behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5fd84311-d2a9-4246-ac9d-1fef8639d89c

📥 Commits

Reviewing files that changed from the base of the PR and between 2ff5624 and 25adc17.

📒 Files selected for processing (5)
  • core/src/autonomous/lib/types.ts
  • core/src/autonomous/target/http.ts
  • core/src/autonomous/target/localScript.ts
  • core/src/lib/localScriptTarget.ts
  • runners/cli/src/commands/hunt.ts

Add hunt.md coverage for the local-script target support added in
this PR — --target-config usage, session/threadId wiring, and the
240s per-turn timeout.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/hunt.md`:
- Around line 29-40: Update the `--endpoint` option description in the options
table to state that the flag is required only when no endpoint is provided by
`--target-config`, while preserving the exception for local-script targets
configured there.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55966ce6-a433-40dd-b25a-2fc9533ea37f

📥 Commits

Reviewing files that changed from the base of the PR and between 25adc17 and 2ba1ae1.

📒 Files selected for processing (1)
  • docs/hunt.md

Comment thread docs/hunt.md
Comment on lines +29 to +40
| Option | Description |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `--endpoint <url>` | Target HTTP endpoint (required unless a local-script target is given via `--target-config`) |
| `--objective <text>` | Attack objective |
| `--objective-file <path>` | Read objective from file |
| `--target-key-env <var>` | Env var with target API key |
| `--target-key <key>` | Target API key directly |
| `--name <name>` | Display name for target |
| `--target-model <id>` | Model value in requests |
| `--stateless` / `--stateful` | History handling mode |
| `--session-field <name>` | Body field for the session id (client-owned, stateful) |
| `--target-config <path>` | JSON file with a run-style `target` block; enables server-owned & header sessions, and local-script targets |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

--endpoint description omits the --target-config HTTP case.

The description says --endpoint is "required unless a local-script target is given via --target-config", but for HTTP targets the endpoint can also be supplied inside the --target-config file, making the --endpoint flag optional in that case too (see endpoint: opts.endpoint ?? baseTarget.endpoint in hunt.ts). The current wording could mislead users into thinking --endpoint is always required for HTTP targets.

📝 Suggested wording fix
-| `--endpoint <url>`           | Target HTTP endpoint (required unless a local-script target is given via `--target-config`)                 |
+| `--endpoint <url>`           | Target HTTP endpoint (required for HTTP targets unless provided via `--target-config`; not used for local-script targets) |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| Option | Description |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `--endpoint <url>` | Target HTTP endpoint (required unless a local-script target is given via `--target-config`) |
| `--objective <text>` | Attack objective |
| `--objective-file <path>` | Read objective from file |
| `--target-key-env <var>` | Env var with target API key |
| `--target-key <key>` | Target API key directly |
| `--name <name>` | Display name for target |
| `--target-model <id>` | Model value in requests |
| `--stateless` / `--stateful` | History handling mode |
| `--session-field <name>` | Body field for the session id (client-owned, stateful) |
| `--target-config <path>` | JSON file with a run-style `target` block; enables server-owned & header sessions, and local-script targets |
| Option | Description |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `--endpoint <url>` | Target HTTP endpoint (required for HTTP targets unless provided via `--target-config`; not used for local-script targets) |
| `--objective <text>` | Attack objective |
| `--objective-file <path>` | Read objective from file |
| `--target-key-env <var>` | Env var with target API key |
| `--target-key <key>` | Target API key directly |
| `--name <name>` | Display name for target |
| `--target-model <id>` | Model value in requests |
| `--stateless` / `--stateful` | History handling mode |
| `--session-field <name>` | Body field for the session id (client-owned, stateful) |
| `--target-config <path>` | JSON file with a run-style `target` block; enables server-owned & header sessions, and local-script targets |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/hunt.md` around lines 29 - 40, Update the `--endpoint` option
description in the options table to state that the flag is required only when no
endpoint is provided by `--target-config`, while preserving the exception for
local-script targets configured there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant