Skip to main content

AW native Agent integration reference

This reference is for integration and maintenance. For installation and daily use, see the user guide. Compare public Hook, MCP and policy-update support in the capability matrix.

AW connects your tool policies and Hook commands to an Agent while preserving its normal interface. You describe the programs and events in aw.yaml; AW starts or reuses a local service, connects the supported native Hooks and keeps execution records after the Agent session ends.

The current Linux source build supports Qoder CLI >=1.1.64, OpenClaw 2026.9.6, QwenPaw 2.2.2.post1 / AgentScope 2.0.10, Hermes revision 952c941e and explicit Claude SDK 0.2.88 workers with bundled CLI 2.1.161, plus Pi Coding Agent/Core SDK 0.84.2 workers and AgentScope SDK 2.0.6 through explicitly wired public factories in a foreground Python worker. Qoder SDK 1.0.56 / Worker 1.1.78 uses managed daemon and Query factories. Other first-release adapters are delivered separately. AW does not install an Agent or configure its model account; retain the framework's native configuration.

Start with the Preview package guide for installation, or the component integration walkthrough for source builds and a policy you can verify without a model. Then use the shared configuration below to verify native tool behavior. Field definitions live in the configuration reference.

For optional lifecycle observation, see AgentSight through aw run.

Current support​

CapabilityStatus
Validate one configuration with all 16 event names✅ Recognizing an event does not install a Hook
Start Qoder CLI through AW✅ Interactive and print entrypoints
Run structured Providers before tools✅ observe and block
Run structured Providers after tools✅ observe; success, error and blocked-attempt coverage depends on the framework
Run native scripts and commands before/after tools✅ Unchanged callback input; byte output and exit status forwarded
Preserve existing Qoder Hooks and their scheduling✅ Default settings and an explicit extra settings file
Keep a shared service and persistent execution metadata✅ On-demand or externally started service
Start OpenClaw through AW✅ a new Gateway with Agent tool hooks
Start QwenPaw through AW✅ the official App/API entrypoint
Start Hermes through AW✅ local chat using an explicitly installed native plugin
Start a Pi SDK 0.84.2 worker✅ Public SDK factory; see worker guide
Start a Claude SDK 0.2.88 worker✅ Explicit public factory Bridge; see worker guide
Start a Qoder SDK 1.0.56 worker✅ Managed daemon and Query factories; see worker guide
Start AgentScope SDK through AW✅ Explicit public factories in one foreground Python worker
Start the other first-release frameworks❌ Separate adapters pending; QwenPaw is distinct from Qwen Code
Use other events, portable ask, input rewriting or OS enforcement❌ Not admitted by the current structured Provider path
Install a Preview package and generate a Qoder/sec-core configuration✅ Preview guide; not a stable release

For Qoder, tool.after maps to successful PostToolUse callbacks. Qoder's PostToolUseFailure is a separate event and is not connected in this adapter. Native Hook commands remain subject to Qoder's own response semantics. Passing through a native approval response does not establish portable AW approval support; interactive approval is not part of this delivery's acceptance.

Install a Preview​

Use the Preview packages for a prebuilt AW + sec-core demo. The source instructions below are for developers.

Use one configuration for four frameworks​

The shared YAML and boolean script give all four targets the same before-block and after-observe policy. Follow the walkthrough to copy them, replace absolute paths and verify the Provider. The commands here use its AW_BIN, AW_CONFIG and AW_DEMO variables. Installed-package users can use the same files with AW_BIN pointing to the installed binary; Python 3 is the sample script's dependency.

Prepare each Agent's supported version and native model configuration first. Only the selected target must be installed to run it. Use separate native profiles, choose their tool working directory explicitly and start from $AW_DEMO/workspace. The shared YAML changes neither model credentials nor native permissions. Test one target at a time initially.

TargetNative prerequisiteLaunch detail
QoderCLI >=1.1.64 with native command Hooks, completed loginInteractive or print; optional --native-settings
OpenClaw2026.9.6, configured JSON and existing state directory; stop any Gateway using that profileAW starts a new gateway run; pass both native paths
QwenPaw2.2.2.post1 / AgentScope 2.0.10, initialized App working directory and modelSet QWENPAW_WORKING_DIR; use App/API, not ACP/TUI
HermesClean official revision 952c941e, installed Python console script and initialized local chat profileInstall the AW plugin once into that profile; pass the same profile when running

Launch Qoder:

cd "$AW_DEMO/workspace"
"$AW_BIN" run --config "$AW_CONFIG" --agent qoder

Launch OpenClaw with your existing absolute native paths:

"$AW_BIN" run --config "$AW_CONFIG" --agent openclaw \
--native-settings /absolute/openclaw.json --native-state-dir /absolute/openclaw-state

Launch QwenPaw with your initialized directory:

QWENPAW_WORKING_DIR=/absolute/qwenpaw-home "$AW_BIN" run \
--config "$AW_CONFIG" --agent qwenpaw -- --host 127.0.0.1 --port 8096

Install and launch Hermes after reading its profile requirements:

"$AW_BIN" install --config "$AW_CONFIG" --agent hermes \
--native-profile /absolute/hermes-profile
"$AW_BIN" run --config "$AW_CONFIG" --agent hermes \
--native-profile /absolute/hermes-profile

For OpenClaw and QwenPaw, keep the server terminal open and wait for AW's native Hook readiness message before sending requests through that Gateway or App. Use OpenClaw's connected client, not agent --local; use QwenPaw's App UI/API, not another CLI entrypoint. Qoder and Hermes use the launched native chat. The framework sections below explain coexistence and supported lifecycle scope.

Verify native behavior​

In another terminal, restore AW_BIN, AW_CONFIG and AW_DEMO from the walkthrough. Set AW_TARGET to the target being tested. Start with absent marker files; for a repeat, use a fresh name. Print the following two prompts and send them as separate turns through that target's native interface:

export AW_TARGET=qoder
printf 'Use your shell tool exactly once to run `printf AW_OK > "%s/%s.allowed"`. Report the result; do not retry or use another tool.\n' "$AW_DEMO/workspace" "$AW_TARGET"
printf 'Use your shell tool exactly once to run `printf AW_DENY_TEST > "%s/%s.blocked"`. Report the result; do not retry or use another tool.\n' "$AW_DEMO/workspace" "$AW_TARGET"

These commands only write demo markers if executed. The second input contains the policy's deny marker. After the turns, check:

cat "$AW_DEMO/workspace/$AW_TARGET.allowed"
test ! -e "$AW_DEMO/workspace/$AW_TARGET.blocked"
"$AW_BIN" status --config "$AW_CONFIG"
ls "$AW_DEMO/state/journal"

Expect AW_OK, an AW policy denial for the second turn, and no blocked marker. Read the relevant journal through AW: select a filename from the listed directory and use its 64-character name without .jsonl as AW_AUDIT_KEY.

export AW_AUDIT_KEY=REPLACE_WITH_EVENT_KEY
printf '{"method":"audit","key":"%s"}\n' "$AW_AUDIT_KEY" | \
"$AW_BIN" request --socket "$AW_DEMO/state/aw.sock"

Files include preparation and event journals. For an event, inspect result.records[0].record.plan: kind is event, and its instance and binding must match the AW instance reported at launch. The before-denial call should have policy_block: true; the completed after step should name Provider check-tool and have status: ok. Audit stores metadata, not raw inputs/results or effect reason text. A closed event has terminal: true. The following checks complete the acceptance record:

CheckEvidence
Allowed toolTool ran, allowed marker exists, before and supported after steps succeeded
Policy blockAW returned a denial, blocked marker absent, before audit records a policy block
Provider failureAfter stopping the old sessions/service, launch a copied config with an absent before-script path; the invocation fails, on_error: block prevents the marker, and audit reports failure rather than a policy hit
CoexistenceKeep one existing native Hook/plugin enabled; confirm its expected behavior still occurs alongside AW
RestartExit Agents, stop AW, relaunch with the original config; new tools are checked and old audit keys remain readable

Record framework version, AW commit, actual commands and audit keys. Synthetic Host tests and startup readiness alone do not complete this table. Model refusal, missing tool invocation or native permission denial is not an AW block. After-event coverage differs by framework: for example, Qoder reports successful tools only. Do not require a fabricated after event for every rejected call.

Stop and clean up​

Exit Qoder/Hermes and use Ctrl-C in the owned OpenClaw/QwenPaw server terminals. Wait for those processes to exit, then stop the shared AW service:

"$AW_BIN" stop --config "$AW_CONFIG"

The stop response acknowledges the request; wait for the recorded daemon PID to exit and its aw.sock to disappear. QwenPaw removes its owned temporary plugin on normal exit. Hermes keeps its installed plugin: ordinary Hermes launches without an AW binding do not activate AW callbacks. To undo installation, stop Hermes and native config writers, inspect the reported backup and displaced files, move plugins/aw-native-hooks outside the profile's plugins directory and restore the chosen pre-install configuration. Preserve any later intentional native changes instead of blindly overwriting them. AW has no native-plugin uninstall command.

Retain audit evidence before deleting the demo directory, following the walkthrough's cleanup. Framework profiles remain separate. Package removal is covered by the Preview guide.

Build and start Qoder​

AW is not yet available through anolisa install or an RPM. Developers can build it on Linux with rustup and the repository's pinned toolchain. Install Qoder CLI separately and verify its version. From the repository root:

cd src/aw
cargo build --locked -p aw-cli --bin aw
qodercli --version
target/debug/aw validate --config crates/aw-service/examples/aw.qoder.yaml
target/debug/aw run --config crates/aw-service/examples/aw.qoder.yaml --agent qoder

The Qoder example uses argv: [qodercli]. To select a particular installation, replace that entry with the absolute path of its executable. --agent qoder selects the named entry under spec.agents; the name is yours to choose, while adapter: qoder selects the framework.

AW requires a semantic CLI version >=1.1.64, the previously tested baseline, instead of one exact release. After surrounding whitespace is trimmed, --version must return a bare SemVer such as 1.1.66, within 1–128 UTF-8 bytes. A leading v, product names, trailing annotations and additional lines are not accepted. A format error states this expectation and shows the observed output. AW records the trimmed output without searching it for a version token. Older or invalid versions are rejected before binding; a prerelease of 1.1.64 is below the minimum, while build metadata is retained. No YAML version field or downgrade is needed for a CLI using the same native Hook contract: --settings, exec-form command Hooks with literal args, PreToolUse/PostToolUse, callback IDs/input and exit 2 for blocking. Version text alone does not certify compatibility; Hook-disabling settings, unsupported launch modes and malformed callbacks still fail. Earlier AW builds that required 1.1.64 must be updated to use this behavior.

For example, name the target qodercli and select your installed executable:

agents:
qodercli:
adapter: qoder
argv: [/absolute/path/to/qodercli]

Merge that block under spec in your complete configuration, then run aw run --config /absolute/aw.yaml --agent qodercli. The target name is separate from the native executable name and version.

This example consumes Hook input and returns {} before and after tools. It adds no restriction and is not a security policy. Ask Qoder to run a harmless read-only tool to exercise both callbacks. A reply produced without a tool call does not exercise them. Arguments after -- go to Qoder, for example:

target/debug/aw run --config crates/aw-service/examples/aw.qoder.yaml --agent qoder -- -p 'Read the current directory name with a tool.'

AW opens Qoder's native terminal interface, then returns to the original shell when Qoder exits. It releases that session's binding and unfinished work. The shared daemon remains available to later sessions using the same configuration.

target/debug/aw status --config crates/aw-service/examples/aw.qoder.yaml
target/debug/aw stop --config crates/aw-service/examples/aw.qoder.yaml

Start AgentScope SDK​

Use a Python environment with agentscope==2.0.6; the SDK is separate from QwenPaw's verified AgentScope 2.0.10 runtime. argv selects the SDK environment's Python and a .py application or -m MODULE. AW verifies that exact SDK version, supplies aw_agentscope and a protected runtime configuration, and awaits a receipt after the real public factory has installed middleware.

From src/aw, with that environment's python3 on PATH:

target/debug/aw run --config crates/aw-service/examples/aw.agentscope.yaml --agent agentscope
target/debug/aw status --config crates/aw-service/examples/aw.agentscope.yaml
target/debug/aw stop --config crates/aw-service/examples/aw.agentscope.yaml

The example uses a deterministic offline model and a local Echo tool. It needs no credentials. Its before Boolean Hook returns false (no block match) and after returns true (observe). Change the before command to printf true to deny the tool; the native final response becomes denied and after observes that denial. Stop the service before editing its configuration.

For an existing application, use AwBridge().create_agent(...) in every public factory, passing the existing middleware list unchanged. Existing trusted argument injection must precede the appended AW middleware. A readiness receipt covers the constructed Agent, not other factories or direct dispatch routes. The factory contract details ordering, budgets, invocation identity and coverage.

Before waits and can block before a tool side effect. After awaits one final ToolResponse, preserving success, error or interruption without repeating stream chunks. Native parameter/permission rejections before on_acting, direct Toolkit calls and external execution requests need separate integration. The launcher supports one foreground worker; forking Gunicorn supervisors and multi-worker/remote deployment are outside this contract. Long-lived processes must manage the service's 1024 event-correlation limit per instance explicitly.

For PreTool retry suggestions and final PostTool compression, copy the SDK lifecycle example, replace all paths and map your Toolkit's exact tool names/argument pointers in sec-core. It runs security checks before Tokenless PreTool, observes the original completed response, then compresses one successful text slot. Keep custom/native after Hooks before the final compression step; with replacement enabled AW runs this chain in declared order. Native and observation Hooks cannot replace the response; only the final admitted structured replacement step can change its model-visible text. Reported Hook failures retain the original input for subsequent steps. Observe-only chains keep native reverse unwind order.

PreTool denies the original call and returns full retry arguments to the model; each new attempt passes native permissions and sec-core again. An attributed RTK retry avoids double compression even with a new call ID. Install RTK separately for PreTool; PostTool works without it. See Tokenless behavior for text-slot eligibility, failure handling and telemetry configuration. Invalid final-response replacement directives report failure and retain the original result.

Start QwenPaw​

Install QwenPaw 2.2.2.post1 with AgentScope 2.0.10 and initialize its model configuration through QwenPaw. The example uses argv: [qwenpaw, app]. Set QWENPAW_WORKING_DIR to the existing initialized absolute directory. AW adds only its owned temporary plugin, preserves existing plugins and native configuration, and removes its plugin after the App exits.

Ordinary launches verify resident middleware on each constructed Agent and open an acknowledged task scope. Before Hooks run forward; after Hooks unwind in reverse. The host controls native tool concurrency. Offloaded tools keep their scope until native generators, Hooks and background completion callbacks finish. Background HTTP output and next-request hints become visible only after all required native callbacks succeed. Denials retain policy text and Tokenless retry arguments. Completed invocations retire their state, allowing long sessions to continue within the bounded active-call limit.

After Hooks receive redacted terminal ToolResponse objects, including AW-denied results. The application credential extension supplies one private execution lease per Agent reply, shared by that reply's tools, and owns its cleanup; see Private execution leases. Redactors must be idempotent across repeated delivery/Hook checks. Authorization-bearing arguments remain unchanged. on_error: report preserves callback error reporting; before block denies execution. For structured Providers and boolean Hooks, after withhold_result prevents unverified result delivery and later dispatch. This action is admitted only on ordinary resident launches. Strict after processing withholds streaming chunks for matching tools. Task-private execution also withholds intermediate chunks until terminal redaction. Native Hook or lifecycle failures fence the Agent even when AW steps use report; restart the failed Agent after correcting the cause. Cleanup timeout or uncertain finish/release acknowledgement fences new requests process-wide and requires a worker restart. Credentials remain owned until native work is confirmed drained. Shutdown shares one ten-second budget with nested cleanup, including cancellation. Event budgets must be 1..55,000 ms; ask/approve directives remain unsupported. Native permissions run before these callbacks; external execution and earlier permission denials have separate native paths.

The startup receipt confirms plugin loading; each Agent additionally passes the actual installation guard before its request starts. Use App/API after the native Hooks ready message. ACP/TUI and multi-worker launches remain rejected. --reloadable retains its separate request-idle update contract. Ordinary launches provide resident child scopes, long-session retirement and explicit updates between fully drained replies. A reused Agent reads the active policy for each new reply after reservation. App startup confirms update readiness before the first reply; startup policy remains active until an update succeeds. Native evidence capture is rejected for ordinary resident launches because that child-scope observation contract is unavailable.

QWENPAW_WORKING_DIR=/absolute/qwenpaw-home target/debug/aw run \
--config crates/aw-service/examples/aw.qwenpaw.yaml --agent qwenpaw \
-- --host 127.0.0.1 --port 8096

The neutral QwenPaw example runs commands before and after tools; it installs no security policy. Use the same aw-provider/v1alpha1 policy configuration across supported adapters. The native Hook output dialect and event coverage remain framework-specific.

An ordinary configuration with no Providers or event steps still installs resident admission and cleanup middleware.

Start OpenClaw​

Install OpenClaw 2026.9.6 separately. The example uses argv: [openclaw, gateway, run]; set an absolute executable path if needed. Supply the existing native JSON configuration and an existing absolute state directory. AW writes a private configuration overlay and retains that directory's authentication and sessions. It refuses conflicting Gateway ownership and does not attach to a running one. AW disables Node compile caching for the owned Gateway to prevent launcher respawns from changing its readiness PID; custom wrappers must exec the Gateway.

Each AW step becomes one native plugin handler. OpenClaw runs before handlers serially by native priority and after handlers concurrently; other plugins retain their priorities. Event budgets must be 1..12,000 ms. Blocked attempts can still produce after callbacks with an error, so after does not imply successful execution. The supported path is Agent execution inside the new Gateway; operator tools.invoke is not a complete before/after entrypoint. AW reports native Hooks ready after the Gateway startup callback, separately from service availability.

target/debug/aw run --config crates/aw-service/examples/aw.openclaw.yaml --agent openclaw \
--native-settings /absolute/openclaw.json --native-state-dir /absolute/openclaw-state

The neutral OpenClaw example runs commands before and after tools; it installs no security policy. Use the same aw-provider/v1alpha1 policy configuration across supported adapters. The native Hook output dialect and event coverage remain framework-specific.

Upgrade the service​

This build uses local protocol aw-service/v1alpha7. A daemon from an older build is rejected with protocol_version before Agent binding or callbacks. Before replacing the executable, exit its Agent sessions and use the old aw stop --config FILE (or --socket ABSOLUTE_PATH) to stop each old daemon. Then update the CLI and daemon together and launch again. Do not delete a live socket or its audit history. The AW configuration and Provider protocol remain aw/v1alpha1 and aw-provider/v1alpha1.

Boolean policy commands​

For a simple rule, let a command read one normalized event JSON from stdin and write only the JSON boolean true or false to stdout, then exit 0. true means the rule matched: AW requests the configured effect. false requests no effect and does not override native permissions. Diagnostics may go to stderr and do not enter the response or audit. Custom hooks default to invoke-only execution: omit the Provider protocol and step operation. AW performs local configuration/effect admission without calling describe or validate_config, then invokes your script only for an admitted event. The script receives no method, request ID or digest and constructs no response object. Explicit protocol: aw-provider/v1alpha1 retains the full structured Provider handshake.

For example, save this rule as $AW_DEMO/check.py; Python is a dependency of this chosen script, not AW core:

import json
import sys

def contains(value):
if isinstance(value, str):
return "12345" in value
if isinstance(value, list):
return any(contains(item) for item in value)
if isinstance(value, dict):
return any(contains(item) for item in value.values())
return False

event = json.load(sys.stdin)
print(json.dumps(contains(event["tool"]["input"])))

Using the Preview prefix and paths from the installation guide, generate the full YAML instead of assembling Provider and step objects by hand. Supply only the Agent entrypoints you need; this example declares both:

"$AW_PREVIEW_PREFIX/bin/aw" config init --prefix "$AW_PREVIEW_PREFIX" \
--config "$AW_DEMO/aw-command.yaml" --state-dir "$AW_DEMO/command-state" \
--qoder "$AW_QODER" --node "$AW_NODE" --openclaw "$AW_OPENCLAW" \
--provider command --check /usr/bin/python3 --effect block \
--reason-code parameter_contains_12345 -- "$AW_DEMO/check.py"
"$AW_PREVIEW_PREFIX/bin/aw" validate --config "$AW_DEMO/aw-command.yaml"
"$AW_PREVIEW_PREFIX/bin/aw" run --config "$AW_DEMO/aw-command.yaml" --agent qoder

No sec-core package or daemon is needed. Use the same generated configuration with --agent openclaw and its existing native profile options. The script sees the same normalized tool fields for both frameworks, while AW translates a block into each native Hook response. The generated transport invokes your command directly. Inside spec, a custom hook can be declared as follows:

providers:
command:
transport:
type: stdio
location: agent
argv: [/usr/bin/python3, /absolute/path/check.py]
timeout_ms: 1000
max_output_bytes: 32
events:
tool.before:
enabled: true
required: true
steps:
- id: check-command
provider: command
effects: [block]
reason_code: parameter_contains_12345
on_error: block

Omitted protocol means aw-hook/v1alpha1; omitted operation means invoke. Both may be declared explicitly. Hook config is omitted or an empty object; pass script options through literal argv. true requests all effects in that step's effects list, optionally carrying its reason_code; false requests none. Effects still require Adapter support. block applies only before a tool; observe can also run after a tool. Full Provider steps use effects as an admitted upper bound and let the Provider choose its returned effects.

The generator creates one required before-tool step with on_error: block and a 1,000 ms command timeout. A nonzero exit, timeout, oversized output, incomplete stdin delivery or a non-boolean response is an execution error, never a false result; the step's failure action applies. Boolean stdout is limited to the smaller of max_output_bytes and 32 bytes including whitespace. The service caps stderr at 65,536 bytes. AW does not insert a shell. Preparation validates the configuration and Adapter capabilities without executing the user command. aw validate checks the desired document offline.

Existing aw policy bridge configurations remain supported, including their private config.on_true effect mapping and full Provider protocol. They need no migration. To switch a custom rule to the default form, point transport argv at the boolean script, omit protocol, config and operation, and put the matched effects and optional reason code on the step. Stop the old service and Agent sessions before switching configurations. Older AW binaries do not accept the new default form; retain the earlier configuration for rollback.

aw config init creates a new private file and never overwrites an existing one. To remove a policy, generate a new base document without --provider (or with --provider none), then exit the old Agents, stop their service and explicitly switch to the new configuration. Provider definitions and associated steps are removed together; editing a running service's file does not reload it. Generic editing of existing YAML is outside this command.

Start Hermes​

Use the official Hermes checkout at revision 952c941e741e922a9be8fc403c8944c6e96318bb. Both installation and launch reject staged or unstaged changes to tracked files; commit or stash local changes and restore the pinned revision before retrying. Unset GIT_DIR and GIT_COMMON_DIR in the parent and profile dotenv environments; the adapter requires Git to discover this checkout normally. Earlier skipped PATH names must be protected against replacement or creation by other users, including sticky writable parents. Nonempty GIT_CONFIG_SYSTEM, GIT_CONFIG_GLOBAL and XDG_CONFIG_HOME values, and any HOME value, must be absolute in both environments. Unset unsupported relative values or replace them with absolute paths. Use UTF-8 working-directory, profile, venv, editable-checkout, AW executable and launch-state paths. Git must resolve to the same canonical executable in cwd and checkout. Both directories use their own effective configuration checks, including local and conditional includes; external filter commands and fsmonitor helpers are refused. The example uses argv: [hermes, chat]; select that installation's Python console script, such as /absolute/hermes/venv/bin/hermes, with an absolute Python shebang. Explicit paths and PATH lookup both require executable regular files; FIFOs, directories and non-executable files are rejected before native probes. Shell wrappers and python -m entrypoints are unsupported. AW uses that interpreter to enter the same native CLI process, preserving its arguments and working directory. Native profile loading determines the effective HERMES_SAFE_MODE, including profile .env overrides; safe mode is rejected, and plugin registration is required before chat can dispatch tools. Initialize its profile and model account through Hermes first. aw agent connect is an explicit one-time installation of the native AW plugin in the selected profile. It saves the original configuration before enabling that plugin, retains unknown configuration fields, and refuses a conflicting non-AW plugin. The native YAML writer edits a private candidate before AW atomically exchanges it with the live config. The command reports the exact initial backup and a permanently retained displaced file (<backup>.displaced) containing the original inode, including later writes through an already-open descriptor. Stop native config writers during installation and rollback, and review both recovery files before restoring. A writer failure or conflict detected before publication leaves the live config unchanged; a conflict detected at the exchange boundary reports failure with the candidate already published and preserves the displaced content for review. Success also requires removing the config staging directory and syncing its parent. A cleanup or sync failure reports the already-published configuration and both recovery paths, without reporting success or rolling back. This exchange does not provide compare-and-swap against writers that ignore AW's lock. Installation does not hot-load or restart existing Hermes services. The canonical profile directory, configuration and bundled plugin files must be owned and not writable by others. If the profile has a .env, it must also be a caller-owned regular file without group/other write permission (0600 or 0644 is accepted). AW checks it without following symlinks before any native probe, including --version, because importing the CLI loads that file. Every ancestor must belong to the caller or root and prevent group/other writes, except sticky directories such as /tmp that protect owned children. AW rejects unsafe paths without changing their permissions. Ctrl-C or SIGTERM cancels native installation probes/writers, removes staging copies and retains the signal exit status. Installation and launch probe errors remain visible before signal exit, including unverified process-group cleanup. Before probing or modifying the profile, installation validates configured native arguments using the same local-chat restrictions as launch. An executable-only argv remains supported; supply chat and its options after aw run's -- in that case. Chat --help/-h exits before plugin readiness, so AW rejects these options in configured arguments and arguments appended to aw run, before native probes or profile writes. Run hermes chat --help directly to view native help. Literal query values such as --query=--help remain supported.

An AW upgrade can change the bundled plugin bytes. If launch or installation reports a mismatched or incomplete plugin, stop Hermes sessions and plugin writers, review <profile>/plugins/aw-native-hooks, and move that directory to a backup outside <profile>/plugins. Rerun the same aw agent connect command to install matching files; the saved directory remains available for inspection or rollback.

An empty providers and events configuration launches with zero AW callbacks. Existing native Hooks still run; AW records the session lifecycle without claiming per-tool policy checks or audit records. Explicit installation remains required.

Subsequent aw run calls retain that profile's credentials, history and working directory. Native before/after callbacks run in Hermes registration order; a before block does not prevent later registered callbacks from running. Post callbacks also observe blocked and failed attempts. AW preserves native Hook parsing and the Agent's own approval flow; portable AW ask is not supported. With AW before callbacks configured, native Hook and tool-execution middleware argument rewrites are refused, including nested execute_code calls. Hermes can apply them after those callbacks, so the resulting arguments have not been checked. Observation-only execution middleware remains supported. Empty and after-only AW routes retain native rewrite behavior. When AW callbacks are configured, forced plugin reload during before checks or tool execution is refused before teardown to retain pending audit callbacks; reload between tools keeps native behavior. This adapter covers local chat, not the native TUI, Gateway or ACP. AW pins the entrypoint with native --cli; full chat options are required, and profile/worktree/resume switches and option abbreviations are unsupported. Event budgets are limited to 55,000 ms and must fit the native callback timeout.

target/debug/aw agent connect --config crates/aw-service/examples/aw.hermes.yaml --agent hermes \
--native-profile /absolute/hermes-profile
target/debug/aw run --config crates/aw-service/examples/aw.hermes.yaml --agent hermes \
--native-profile /absolute/hermes-profile

The neutral Hermes example runs commands before and after tools; it installs no security policy. Use the same aw-provider/v1alpha1 policy configuration across supported adapters. The native Hook output dialect and event coverage remain framework-specific.

Connect your programs​

Each named object in spec.providers describes a program. An event step refers to its name through provider. Choose the protocol for the program you have:

ProtocolInput and resultStep fields
aw-hook/v1alpha1 (default)The command reads one normalized event and returns JSON true/false; AW projects the configured effects on a match without a script handshakeeffects, on_error; optional reason_code; omit operation or use invoke
aw-provider/v1alpha1AW performs describe, validate_config and invoke; responses contain checked candidate effectsoperation, effects, on_error
native-hook/v1alpha1The command receives the selected adapter's native callback bytes; the adapter handles stdout, stderr and exit statusnative: {}, on_error; omit operation and effects

For a native Hook, replace the example's transport.argv with the executable and literal arguments for your script. AW does not insert a shell; shell syntax requires an explicit /bin/sh -c .... Keep config: {} for this protocol: there is no Provider configuration exchange. Qoder-specific native output is not automatically portable to another framework.

For structured policies, use aw-provider/v1alpha1 and put Provider-owned settings in config. The runnable policy example shows before-tool observe/block and after-tool observe. Build its executable with cargo build --locked -p aw-provider --example policy and run from src/aw because its command path is relative. Its blocked tool name is illustrative; replace it with a tool actually used by your Agent when testing a block. The sample Provider is not sec-core.

Raw commands use the actual callback environment, including profile-loaded variables. Structured Providers keep the environment pinned at binding. Environment contents are excluded from events and audit.

timeout_ms limits one command; default_event_budget_ms limits the whole event. The Qoder launcher accepts event budgets from 1 to 55,000 milliseconds. The output ceiling bounds the returned bytes. on_error controls execution failures, separately from a Provider's successful policy block. For native commands, an ordinary nonzero exit remains native output, not an AW transport failure. Qoder interprets exit 2 as a blocking response where supported and other nonzero exits as nonblocking errors. An observe result adds no permission and does not override Qoder's own tool permissions.

Keep existing Hooks and scheduling​

Qoder continues to load its normal user, project and local settings. AW creates session-specific callback entries without editing those files. To include an existing JSON file that you normally pass through Qoder's --settings, use:

target/debug/aw run --config ./aw.yaml --agent qoder --native-settings ./qoder.settings.json

AW preserves that file's other fields and Hook entries, adds its callbacks and passes the merged settings to Qoder. Each AW step becomes a separate synchronous native Hook. Qoder determines serial or parallel execution, including how the new callbacks coexist with other matching Hooks.

Set spec.agents.qoder.qoder.sequential: true to mark the generated groups as sequential. If any matching synchronous Qoder group requests sequential execution, all matching synchronous Hooks run in sequence. Omitting this field or setting it to false does not override an existing group's true value. Steps in one native event share the AW budget; later callbacks do not reset it.

This adapter supports scripts whose input remains unchanged across AW steps. Sequential input-rewrite chains are not supported: if a command returns updatedInput, a later callback with changed input is rejected and follows that step's on_error. Forwarding native bytes does not promise every native effect combination or approval flow.

The launcher rejects settings and arguments that would disable or replace its Hooks rather than overriding a user's disabled-hook choice. This includes raw --settings, --setting-sources, --headless-fast-hooks and conflicting native settings. Custom configuration roots, alternate Qoder configuration-directory modes, resumed sessions, remote sessions and worktree launch are outside the current adapter's scope. Pass short options separately rather than combining them, and launch from the intended working directory. Existing native Hooks remain responsible for their own behavior and audit records.

Service lifetime and records​

spec.daemon.startup: on_demand starts a service if none exists at the selected endpoint; external requires one to be running already. An existing service is reused only when its protocol and exact configuration revision match. With endpoint: auto and state_dir: auto, AW selects a private configuration-specific directory under XDG_RUNTIME_DIR, or under /tmp/aw-UID when that variable is unset. Explicit paths must be absolute and agree on the aw.sock location.

The service retains its bootstrap configuration snapshot. Editing aw.yaml does not reload it. Supported ordinary resident and opt-in restricted instances can use explicit idle policy updates; these change the instance Host without changing service identity. Stop a service using its original file or socket before retiring that configuration; auto paths for changed configuration may select a different service. Exiting an Agent does not stop other sessions or the shared daemon. The local endpoint is a same-user boundary, not a sandbox.

CommandPurpose
aw validate --config FILECheck syntax and static references without executing programs
aw run --config FILE --agent TARGET [OPTIONS] -- ARGSStart the configured Agent and connect supported Hooks
aw agent connect --config FILE --agent TARGET [--native-profile PROFILE]Explicitly install the Hermes native plugin into an existing profile; Qoder, OpenClaw, QwenPaw, Pi, Claude SDK and AgentScope SDK reject this command
aw update --config CURRENT --agent TARGET --policy NEXTUpdate a supported ready, idle instance; busy rejects without queuing
aw serve --config FILE --state-dir ABSOLUTE_DIRRun the service in the foreground
aw status --config FILE or aw status --socket ABSOLUTE_PATHInspect the selected service without starting it
aw stop --config FILE or aw stop --socket ABSOLUTE_PATHRequest graceful shutdown
aw request --socket ABSOLUTE_PATH [--timeout-ms 1..60000]Send one developer operation JSON object from stdin; default 5,000 ms

--agent TARGET selects a named entry in spec.agents; its adapter selects the implementation. This build implements Qoder CLI >=1.1.64, OpenClaw 2026.9.6, QwenPaw 2.2.2.post1 / AgentScope 2.0.10, Hermes revision 952c941e and explicit Claude SDK 0.2.88, Pi SDK 0.84.2 and AgentScope SDK 2.0.6 workers. aw agent connect validates the configuration and dispatches to that adapter. Hermes supports persistent plugin installation; Qoder, OpenClaw, QwenPaw, Pi, Claude SDK and AgentScope SDK reject it. This command does not install AW packages or Agent software, start an Agent, or configure model credentials. aw run does not implicitly call agent connect. The legacy aw install --config FILE --agent TARGET [--native-profile PROFILE] syntax keeps the same native-registration behavior. Software installation uses aw install --prefix ABS_DIR --bundle ABS_DIR; the two option sets cannot be mixed.

Adapter-specific optionCommandCurrent support
--native-settings JSON_FILErunQoder: optional extra JSON settings merged with generated Hooks; the original file remains unchanged. OpenClaw: required existing native JSON configuration
--native-profile PROFILErun, agent connectHermes: required existing absolute profile for run and agent connect. Qoder and OpenClaw reject it
--native-state-dir DIRECTORYrunOpenClaw: required absolute native state directory. Qoder rejects it; this is not the AW service's --state-dir

The shared command parser recognizes these adapter-specific options, but that alone does not enable them for a framework. Pi, Claude SDK and AgentScope SDK reject all these native override options. QwenPaw also rejects them and uses QWENPAW_WORKING_DIR. Pass supported AW options before --; arguments after it are forwarded literally to the Agent. agent connect accepts no Agent arguments. Use aw --help to see the command syntax and current support.

In a source checkout, use target/debug/aw for aw. aw hook is an internal callback generated by the launcher; users do not need to construct it. Service records contain metadata without tool input/results, private Provider configuration or raw stdout/stderr. A running service or completed invocation does not prove native policy adoption. Killed or disabled native callbacks can miss checks; this version does not provide final/protected execution or an OS fallback.

Containers and persistent data​

Prepare AW binaries, supported framework versions, policy programs and default configuration in the image. At runtime, supply the native profile and launch the selected Agent through aw run; a Dockerfile cannot retain a live service or a usable session binding. For Hermes, install the matching plugin into the actual runtime profile before launch. A mount over an image directory may hide files installed there during the image build; see Docker's mount behavior.

DataContainer handling
AW binaries, scripts, fixed aw.yamlBake into the image or supply read-only; save editable configuration outside the disposable layer
<state_dir>/journal/Persist when execution history must survive container replacement; audit is enabled and metadata-only in this version
Socket, locks, launch-* and session bindingsRuntime artifacts, not recoverable sessions; each service/Agent launch creates fresh runtime state
Native profiles, credentials, conversations and workspacesPersist according to the selected framework's requirements; these are not AW audit data
sec-core backend dataManaged by sec-core independently of AW; see the Preview backend setup

AW always needs writable local state even when no history is retained across container replacement. For durable history, set an absolute daemon.state_dir on a local persistent filesystem that honors fsync. Keep endpoint: auto to use its aw.sock. The directory must belong to the runtime UID with mode 0700; its parent must also belong to that UID and must not be writable by other users. Prepare volume ownership for the actual container UID before starting AW. The default auto directory under XDG_RUNTIME_DIR or /tmp is temporary storage.

Plan the lifetime of long-running Gateway/App launches around this Preview's limit of 1,024 distinct correlated native events per AW instance. With both before and after enabled, each completed tool call consumes two entries. On exhaustion, opening a new correlated event fails with native_event_limit before any step script runs. This is an event-opening failure, not a Provider result. The AW callback bridge converts the failure into the selected framework's native response; a before callback with on_error: block requests denial. Final tool behavior follows that framework's native Hook semantics. Exit and relaunch the AW-managed Agent to create a new instance; starting another native conversation does not reset this limit.

The current state directory contains both the journal and runtime artifacts. If you persist the entire directory, graceful shutdown removes the owned socket, but a forced container kill can leave it behind. Before reuse, confirm that its old service is gone and remove only that stale aw.sock; do not delete live endpoints or the journal. Do not share one state directory between independent AW daemons. Arrange container shutdown to exit Agents, stop AW and wait for the owned processes. A retained journal supports historical queries, not resuming or replaying interrupted tools. Docker volumes survive container removal; their deletion is a separate action described in the volume lifecycle.

Common setup failures​

SymptomNext check
Static validation passes, but launch failsCheck selected native version/entrypoint, executable paths and Provider private configuration; validation does not execute them
Unsafe state/profile pathCheck runtime UID, directory permissions and the explicit native path; use a private directory owned by that user
Configuration changed but behavior did notExit old Agents, stop the old service with its original config/socket, then launch the edited file
Hook readiness failsRead the native launch error; confirm the required profile/plugin and supported version before sending requests
No audit after a model responseConfirm a supported tool actually reached the callback; native permissions or a model refusal can prevent invocation
Existing socket after forced terminationConfirm the old service has exited, then remove its stale socket as described above

Run the local service demo​

This demonstration needs no Agent account or model request. From src/aw:

cargo build --locked -p aw-provider --example policy
AW_DEMO_ROOT="$(mktemp -d "$PWD/target/aw-demo.XXXXXX")"
printf 'Socket: %s\n' "$AW_DEMO_ROOT/state/aw.sock"
target/debug/aw serve --config crates/aw-service/examples/aw.yaml \
--state-dir "$AW_DEMO_ROOT/state"

In a second terminal in src/aw, use the printed absolute socket path:

AW_DEMO_SOCKET=/absolute/socket/path/printed/above
target/debug/aw status --socket "$AW_DEMO_SOCKET"
cargo run --locked -p aw-service --example local -- "$AW_DEMO_SOCKET"

The example supplies synthetic capabilities and events. It checks read_demo, blocks delete_demo, observes an after event and prints audit keys. No native Agent or tool is started. Replace AUDIT_KEY with a reported preparation key or event ID to inspect its records:

printf '%s\n' '{"method":"audit","key":"AUDIT_KEY"}' | \
target/debug/aw request --socket "$AW_DEMO_SOCKET"
target/debug/aw stop --socket "$AW_DEMO_SOCKET"

terminal: false means no terminal record is present; the operation may be active or interrupted. A timed-out call must not be retried as a new step. The stop response acknowledges the request; wait for the foreground service to exit before removing this demo directory in its original terminal:

rm -r -- "$AW_DEMO_ROOT"

Normal shutdown retains audit history and removes the owned socket. After a forced kill, AW reports a stale socket instead of deleting it automatically. Verify that the old service has stopped before removing its owned aw.sock; keep its lock and journal when retaining the service directory. Restarting creates a new service identity; historical records remain queryable without resuming or replaying old events.

The configuration reference covers every field and event name. The local service contract describes operation JSON, deadlines and lifecycle for Adapter developers.

Connect sec-core scanning through aw-provider-sec-core; see the Provider guide.

Model-visible PostTool compression uses the optional Tokenless Provider.

Start a Pi SDK worker​

For a high-code Pi application, use adapter: pi with the public Coding Agent/Core SDKs 0.84.2 and Node.js >=22.19.0. AW owns a foreground SDK worker and connects both tool Hook points. Install those SDK packages in your application using your normal dependency workflow, and retain your application's model/auth settings. The native Pi CLI is a separate entrypoint and is not launched by this adapter.

Expose your existing public factory and foreground task in an absolute .mjs application module:

export async function createSession({ sdk }) {
return sdk.createAgentSession({
cwd: process.cwd(),
agentDir: "/absolute/existing-pi-agent-directory",
});
}

export async function run(session, { args }) {
await session.prompt(args.join(" "));
}

Copy the Pi example, set its argv to [node, /absolute/application.mjs], and configure your before and after policies. Its included commands are neutral. Then run:

cd src/aw
target/debug/aw validate --config /absolute/aw.pi.yaml
target/debug/aw run --config /absolute/aw.pi.yaml --agent pi -- 'Your task'
target/debug/aw status --config /absolute/aw.pi.yaml
target/debug/aw stop --config /absolute/aw.pi.yaml

--agent selects your configuration target; its adapter remains pi. --native-settings, --native-profile and --native-state-dir are not Pi SDK options. AW validates the installed SDK versions before binding, waits for the factory and installed callbacks, and then reports AW pi native hooks ready. The application task must stay in that foreground worker. For replacement new/resume/fork/import sessions, use the createSession supplied to run so each factory result gets the same Hook composition. Existing before denial/mutation and after replacement remain authoritative; AW after observes the final result. Blocked or invalid/pre-aborted tools have no after callback in Pi.

For Tokenless, use the Pi SDK target in the shared optimization example. Keep security checks before pre_tool and observers/custom Hooks before the final post_tool replacement. Retry reasons reach the model; only a fresh tool call can execute the suggested command. PostTool replaces one successful text block while preserving SDK metadata; report failures retain the original result. Raw native Hooks cannot supply replacements. AW uses a fresh invocation UUID for pairing, retains the original tool ID in native, and closes events on early denial.

See the adapter contract for exact callback semantics, identity, cancellation, capacity and reproducible SDK acceptance. Exit Agents and stop the original service before changing policy; retain/remove the audit state using the same local-service cleanup procedure.

Start a Claude SDK worker​

Use Claude Agent SDK 0.2.88 with its bundled CLI 2.1.161. The Adapter launches one foreground Python script or -m module and requires the worker's public factory to use aw_claude.create_client(options). Model credentials remain SDK settings. AW supplies this module on PYTHONPATH for the launched worker; it does not inject hooks into an arbitrary SDK application automatically.

For the source example, prepare the selected Python environment and run from src/aw. The MCP pin addresses a reproduced incompatibility with MCP 2.x:

python3 -m venv target/claude-sdk-venv
target/claude-sdk-venv/bin/pip install -r tests/claude-sdk-requirements.txt

Copy crates/aw-service/examples/aw.claude-sdk.yaml to your configuration and set its Agent argv interpreter to the absolute target/claude-sdk-venv/bin/python path. Keep the foreground example worker path or replace it with your script. Its options factory should use this pattern after constructing its normal MCP tools, permissions, resume settings and existing hooks:

from aw_claude import create_client

async with create_client(options) as client:
await client.query(prompt)
async for message in client.receive_response():
handle_message(message)
target/debug/aw run --config /absolute/aw.claude-sdk.yaml --agent claude -- 'Use echo to return hello.'
target/debug/aw status --config /absolute/aw.claude-sdk.yaml
target/debug/aw stop --config /absolute/aw.claude-sdk.yaml

Claude SDK event budgets must be 1..55,000 ms. Each tool.before or tool.after route uses events.<name>.budget_ms when set, otherwise execution.default_event_budget_ms. aw run enforces this adapter-specific limit after static configuration validation.

The worker's options cwd must equal the launch cwd. Readiness is published after the real SDK initialization handshake accepts callbacks. AW observes or denies PreToolUse; PostToolUse and PostToolUseFailure both supply after observations. Denied tools do not produce a synthetic after. Existing hook vetoes and native permissions remain effective. Existing pre-hook updatedInput and permission updated_input changes are denied, because concurrent callbacks cannot establish that AW inspected the final arguments. When tool.before is active, nonempty PermissionRequest matchers are rejected before initialization; use supported PreToolUse or can_use_tool callbacks instead. Caller-specified permission_prompt_tool_name is also rejected with tool.before; SDK-owned stdio permissions for the wrapped can_use_tool remain supported. Caller cli_path overrides and missing bundled binaries are rejected. The worker must not detach or start multiple SDK clients/tasks within one launcher binding; Gunicorn and business deployments need their own task lifecycle integration.

CLI 2.1.161 emits PostToolUse for SDK MCP isError: true responses and omits that flag from callback data. After observation therefore does not classify every business failure. Native Bash execution failure does use PostToolUseFailure. See the Adapter contract for identity, cancellation and validation limits.

For model-free native adoption checks, build AW and use the real SDK/bundled CLI against the local deterministic HTTP fixture. The test creates its own profile and creates private UDS directories under the output directory. It removes them after stopping owned services and rejects socket paths over 107 bytes:

target/claude-sdk-venv/bin/python tests/test_claude_sdk_native.py --aw target/debug/aw --output target/claude-sdk-native

Once the evidence and environment are no longer needed, remove only the created target/claude-sdk-native and target/claude-sdk-venv directories. The AW policy is a demo, not a production security rule; business deployment is separate from these local native checks.

The options factory requires setting_sources=[], no settings override and no CLI plugins. extra_args allows only strict-mcp-config: None; the SDK native strict_mcp_config option is also preserved. Model/authentication options are unchanged. Applications which discover project skills through settings sources need an explicit integration design before using this restricted first bridge. The direct task-worker mode permits one client per worker. Use the resident entrypoint below for multiple task clients.

The claude-sdk adapter supports this restricted foreground worker factory; aw agent connect does not provide persistent installation for it, and all --native-* overrides are unsupported.

Enable Bash PreTool and PostTool optimization​

Use the shared Tokenless configuration, select its claude target, and replace the absolute executable, worker and state paths. The source claude-worker.py example explicitly exposes and allows native Bash in addition to its echo tool. Keep your application's own permission policy when composing its factory. See the Tokenless guide for Provider installation, RTK requirements and statistics settings.

PreTool blocks the original Bash attempt and returns retry arguments to the model; the new call passes all policies and native permissions again. PostTool replaces only completed, non-image, non-interrupted Bash stdout. Put completion observers and custom steps before the single final replace_result step. They see the original result; stderr and other native fields are retained. Failure/report keeps the original result. MCP tools on this native callback path remain observable but are not compressed because the native callback does not expose reliable business-error status.

After building AW, its Tokenless Provider and the current Tokenless Protocol v2 CLI, extend the native acceptance run (RTK must be available on PATH):

cargo build --locked --manifest-path ../../providers/tokenless/Cargo.toml
target/claude-sdk-venv/bin/python tests/test_claude_sdk_native.py \
--aw target/debug/aw --output target/claude-sdk-native \
--tokenless ../tokenless/target/debug/tokenless \
--tokenless-provider ../../providers/tokenless/target/debug/aw-provider-tokenless

Use one security policy for resident SDKs​

The shared SDK security example selects Qoder SDK or QwenPaw through --agent. Replace every absolute path, initialize the native runtime as described in its section, and start sec-core with the configured socket before launching:

aw validate --config /absolute/path/aw.sdk-security.yaml
aw run --config /absolute/path/aw.sdk-security.yaml --agent qoder-sdk -- "your prompt"
QWENPAW_WORKING_DIR=/absolute/qwenpaw-home aw run \
--config /absolute/path/aw.sdk-security.yaml --agent qwenpaw

The sec-core step matches only native:qoder-sdk:Bash and native:qwenpaw:execute_shell_command. AW projects the selected target's steps before capability admission and Provider preparation. Other tools still use the custom before/after checks without acquiring an implicit sec-core scan. Add explicit selectors and matching Provider tool mappings when extending coverage.

The custom scripts use the boolean Hook contract: read one normalized event from stdin and print a JSON boolean with exit status zero. In this example, before true requests block; after true records observe. After false produces no effect and is still a successful check. A failed result check must exit nonzero; on_error: withhold_result then prevents delivery and subsequent dispatch. Native permissions and the caller's daemon/App lifecycle remain in force. Use ordinary aw run and explicit idle updates between closed Queries or completed replies. Private credentials need the explicit execution factory in application code; this YAML installs the security policy.

Start a Qoder SDK worker​

Use adapter: qoder-sdk with @qoder-ai/qoder-agent-sdk@1.0.56, its bundled Worker 1.1.78 (global), and Node.js 22 or newer. Install the SDK in the worker's application directory. Configure the target in your existing AW configuration:

agents:
sdk:
adapter: qoder-sdk
argv: [node, /absolute/application/worker.mjs]

The worker selects AW's daemon and Query factories when launched through AW. The same worker can run directly with Node and the native SDK factory:

import { startDaemon, query, accessTokenFromEnv } from "@qoder-ai/qoder-agent-sdk";
import { pathToFileURL } from "node:url";

const prompt = process.argv[2];
if (!prompt) throw new Error("A prompt is required");
const bridge = process.env.AW_QODER_SDK_BRIDGE
? await import(pathToFileURL(process.env.AW_QODER_SDK_BRIDGE)) : undefined;
const makeQuery = bridge ? bridge.createQuery : async (request) => query(request);
const makeDaemon = bridge ? bridge.startDaemon : startDaemon;
const daemon = await makeDaemon({ auth: accessTokenFromEnv() });
let current;
try {
current = await makeQuery({ prompt, options: {
daemon, cwd: process.cwd(), settingSources: [],
} });
for await (const message of current) console.log(JSON.stringify(message));
} finally {
await current?.return();
await daemon.stop();
}

Run aw run --config aw.yaml --agent sdk -- "your prompt", or run node /absolute/application/worker.mjs "your prompt" for native startup without AW. Use your normal SDK authentication; keep credentials out of configuration files. The caller starts and stops the daemon and closes each Query. Under AW, use both bridge factories: startDaemon() validates runtime selectors and the Hook-disable environment before starting the pinned daemon; createQuery() rejects daemons created directly through the SDK because their launch context cannot be verified. Daemon startup alone does not publish Query readiness. AW keeps the foreground-worker lifecycle and releases its binding on worker exit.

AW registers per-Query callbacks before initialization, then reports readiness after the SDK handshake. tool.before supports observation and denial; tool.after observes successful and failed native tool calls. Ordinary resident Queries admit on_error: withhold_result: a Post failure or timeout closes the native Query before replying to the callback, withholds the unverified result and stops subsequent dispatch. Tool side effects that occurred before Post remain in effect. on_error: report reports the failure and allows normal result handling. Admission allows on_error: block only for tool.before. Native permission denials retain precedence. Result replacement remains unsupported. Existing SDK hooks are retained; argument rewrites and permission-hook overrides are rejected so a tool cannot execute different arguments from those checked.

Use settingSources: [] and no settings, plugins, extra CLI flags or runtime transport overrides. Hook-disable flags are parsed with the bundled Worker's whitespace normalization and rejected in both process and launch environments. Ordinary createQuery() opens an acknowledged resident scope for each Query, with its canonical working directory and a captured environment. Direct Queries can use distinct environments. A shared daemon owns one immutable environment and its current credentials; its Queries reject env and auth overrides. Use native daemon.auth(payload) to replace the daemon's credentials at runtime. daemon.stop() joins active credential callbacks from every installed payload, including callbacks still running after their credentials were replaced. Use separate daemons or direct Queries when native credentials must differ. Managed Queries share the configured policy. Native SDK Queries created outside the bridge remain unprotected by AW.

Ordinary Queries require evidence mode: off: child scopes cannot inherit an AgentSight observation root. Launch admission rejects enabled evidence before readiness.

The bridge returns a frozen public Query facade. Native parser, transport, callback maps and Hook-routing mutation methods are private. Worker partial output is enabled internally so model tool identities arrive before dispatch; stream_event messages are hidden unless includePartialMessages: true was requested. Callback data cannot register a tool identity. Completed tools retire after the native command queue barrier and original callback finalizers finish. Early return, cancellation and transport failure join native close and original Hook, permission, credential, model and MCP callbacks before retiring the scope. Memory generation/consumption and skill-evolution gate/result callbacks are also joined, along with in-flight sendHostAction() work, including its native fallback cleanup. Closing rejects new Host actions. A dispatch failure can retire its scope after native drain and confirmed credential revocation. Native cleanup failure retains ownership; lease cleanup failure or timeout also fences new work process-wide until worker restart. Native authentication callbacks keep their own lifecycle. Ordinary Queries may pass options.awExecutionFactory to acquire a versioned private lease for each managed MCP handler. The bridge consumes this option before calling the SDK and exports PRIVATE_EXECUTION_VERSION, executionEnvironment() and hookEnvironment(). Handlers explicitly consume the private environment; results are redacted before Post and delivery. The pinned SDK has no such environment injection seam for built-in Bash. See Private execution leases for the public factory contract and cleanup ordering.

For in-process MCP, use await bridge.createSdkMcpServer(options) and provide a fresh managed server per Query. Native schemas, annotations, permission settings and structured result envelopes are retained. A schema that changes approved arguments through defaults, transforms or unknown-key stripping is rejected before the application handler. Strict Pre rejection prevents handler entry. Ordinary resident MCP calls use the same admitted Post withhold_result barrier as native tools. MCP child invocations bind the native control request, exact server/tool/arguments and owning Query. parentIdentity: "query" is the default: the pinned Worker does not expose the originating model tool ID in that MCP request. parentIdentity: "required" rejects before native server creation. See Structured MCP dispatch for metadata and complete result-envelope handling. Result replacement remains unsupported.

Strict Post and private execution leases require ordinary resident mode. After all Queries close and finish cleanup, an explicit idle update affects the next Query. Open Queries remain busy even between turns; they keep their original policy. Managed startDaemon() confirms update readiness and the launch receipt before the first Query, allowing the daemon to remain prewarmed without a dummy Query. The startup policy remains active until an update succeeds. New Queries snapshot the active revision after reservation. Keep task dispatch paused while provisioning Hook files and updating policy, then submit work after success.

The separate restricted --reloadable mode rejects strict Post and private execution leases. For compatible policies, update existing Queries without restarting the daemon by opting in with aw run --config aw.yaml --agent sdk --reloadable. Keep using the same createQuery factory and caller-owned startDaemon(); an asynchronous input iterator keeps a Query available between requests. Use aw update --config aw.yaml --agent sdk --policy desired.yaml to explicitly activate a complete candidate policy through the shared CLI and UpdatePolicy transaction.

In this restricted mode, all managed Queries in the instance must be idle. Submitted/queued messages, model waits, native callbacks and Query close cleanup keep the instance busy. A result message alone is not an idle acknowledgement: inspect aw service status --config aw.yaml and retry an explicit update after the request count returns to zero. Pre-commit failures preserve the active policy. A terminal audit failure after commit blocks dispatch and stops the service; restart before reuse. Stable callbacks are registered even for an empty initial policy; additions and removals affect the next request on existing Queries. Native permissions remain authoritative.

Reloadable Queries require an explicit daemon and msg_lifecycle_v1 support. The bridge retains request admission until the native command queue has drained, and awaits native close() plus in-flight callbacks on early return or error. Ordinary Bash commands can automatically move to the background on timeout. Their request remains busy until the child exits and the native completion notification turn finishes cleanup. Closing the Query first stops those tasks through the native stopTask() API before releasing admission. Use prompt input or streamInput to submit work. Goal options, setGoal, resumeGoal, host-action scheduling, backgroundTasks, CreateGoal, ScheduleWakeup, CronCreate and run_in_background tools are rejected because they can schedule work outside that admission boundary. Timer-created turns bypass the input iterator, so creating a scheduled wakeup or cron job is unsupported; CronList and CronDelete remain available. Worker 1.1.78's UpdateGoal only completes or blocks an existing goal; it does not activate one and remains available. Query creation also rejects custom agents definitions with background: true or isolation: "worktree". The Agent tool's isolation: "worktree" input is denied before execution because worktree execution is outside the supported binding boundary. Foreground definitions and default-isolation Agent calls remain allowed. Ordinary aw run uses the resident scope path and retains native goal APIs. External PID attach/detach, independently selected per-session AW policies and steer are not provided by this integration.

In ordinary mode, Worker 1.1.78 sends worktree child callbacks with the parent Query's cwd and a child agent_id, although tools execute in the worktree. AW observes those callbacks; it does not reconstruct the actual execution working directory. Worktree execution is not supported for policies that depend on that directory, and ordinary mode does not reject it at Query creation. Reloadable mode rejects it before execution as described above.

The reloadable bridge keeps reading native output to observe cleanup even when application reads pause. Its output buffer is not bounded: continuously consume Query output while submitting work, including across result messages. If the application stops consuming, stop submitting and await close() or return(); these discard buffered output and await native cleanup. This mode does not provide backpressure for an indefinitely paused consumer.

The reloadable Qoder SDK path retains 1024 Hook correlation entries per instance, and 64 active claims per request/tool claim set. Completed request claims are released; Hook correlation entries are retained until instance release. These are not a count of supported lifetime turns. Bound instance lifetime and concurrent submissions; policy updates do not reset correlation history.

Resident Pi and Claude SDK hosts​

Install matching AW, Providers and pinned SDK packages in the image. Use the shared configuration example, replace its paths and select the target:

aw run --config /etc/aw/aw.yaml --agent claude
aw run --config /etc/aw/aw.yaml --agent pi

For Claude, set the target argv to the application's Python interpreter followed by -m aw_claude -- -m application. A foreground Gunicorn host can use -m aw_claude -- -m gunicorn -c conf/gunicorn.py main:app; create task clients inside workers after fork. In the common client factory:

from aw_claude import create_client

client = create_client(options)
try:
await client.connect()
await client.query(prompt)
async for message in client.receive_response():
yield message
finally:
await client.disconnect()

Sequential queries reuse a client after its response and callbacks drain; each client retains its conversation. Resume/session-store options remain available. Concurrent work uses independent clients with separate cwd/env. Each query accepts a string or a finite one-message AsyncIterable; multi-message input is rejected before dispatch. Submit successive turns with sequential queries. Both receive_response() and receive_messages() drain callbacks before delivering ResultMessage. EOF without that message raises an error; keep disconnect() in finally so unfinished queries still receive an interrupt.

For in-process MCP tools, also import create_sdk_mcp_server from aw_claude and create a fresh server for each client. Keep SDK tool definitions and business closures. The managed MCP contract covers single successful text replacement, reliable errors and explicit correlation.

Resident Pi and Claude hosts reject enabled AgentSight evidence before publishing readiness. Set evidence mode: off for resident hosts; legacy task mode continues to support evidence.

For Pi, export the existing SDK session factory and a resident host entrypoint:

export async function createSession({ sdk, cwd }) {
return sdk.createAgentSession({ cwd /* application model/tools/options */ });
}
export async function runWorker({ withSession }) {
await serveRequests(async (request) => {
return withSession({ cwd: request.workspace, environment: request.environment },
async (session) => session.prompt(request.prompt));
});
}

serveRequests is the application's server. Install business callbacks before returning from the factory. Close sessions at task completion; concurrent prompts on one session are rejected. Avoid creating unmanaged SDK clients elsewhere.

The example composes custom Hooks, sec-core Bash scanning and Tokenless PreTool/ PostTool. Start the sec-core daemon separately and supply a real custom Hook. Opaque MCP tools need explicit security semantics. Resident mode stops tasks on callback errors even for on_error: report; policy denial remains a normal native denial. A failed Claude interrupt terminates AW's owned foreground process group, so configure supervisor restart. Drain requests before sending termination and set the target's shutdown_grace_ms to allow time for the application's remaining cleanup. The outer launcher defaults to a two-second grace from its first signal forwarding until forced cleanup starts. See shutdown grace for scope and limits. Schema/BeforeModel and Retrieve require the request/effect admission and authorization work listed in the resident contract.