Rename inbound delivery modes#898
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
✅ Files skipped from review due to trivial changes (4)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR refactors the broker and SDK from per-worker "session mode" (human/passthrough) to "inbound delivery mode" (auto_inject/manual_flush). Type definitions and state tracking are replaced throughout the broker, the SDK client API methods are updated, and CLI commands (drive and passthrough) are modified to use the new delivery-mode endpoints. Documentation is updated across broker API, CLI reference, and TypeScript SDK guides. ChangesSession Mode to Inbound Delivery Mode Refactoring
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
web/content/docs/reference-broker-api.mdxParsing error: Expression expected. web/content/docs/reference-cli.mdxParsing error: Expression expected. web/content/docs/typescript-sdk.mdxParsing error: Expression expected. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 88b12e6d48
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let Some(mode) = InboundDeliveryMode::parse(&body.mode) else { | ||
| return api_error( | ||
| axum::http::StatusCode::BAD_REQUEST, | ||
| "invalid_mode", | ||
| format!( | ||
| "unsupported session mode '{}' (expected 'passthrough' or 'human')", | ||
| "unsupported inbound delivery mode '{}' (expected 'auto_inject' or 'manual_flush')", |
There was a problem hiding this comment.
Preserve legacy
/mode payload values for alias clients
The /api/spawned/{name}/mode route is kept as a compatibility alias, but this handler now parses only auto_inject|manual_flush; older SDK/CLI clients that still send { "mode": "passthrough" } or { "mode": "human" } to /mode will get 400 invalid_mode and fail to attach/restore mode. That breaks the stated back-compat path for existing clients while they migrate.
Useful? React with 👍 / 👎.
| if (mode !== 'view' && mode !== 'drive' && mode !== 'relay') { | ||
| deps.error(`Error: --mode must be one of view|drive|relay (got '${String(options.mode)}')`); |
There was a problem hiding this comment.
Allow
--mode passthrough in spawn-and-attach path
This validation now rejects new --attach --mode passthrough, even though the interactive command still exposes passthrough as an alias of relay. Existing automation that uses the old attach-mode flag will now fail early with a usage error, creating an inconsistent migration experience across CLI entry points.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main.rs (1)
4314-4339:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon't collapse queue eviction into a plain queued result.
InboundDeliveryDispatch::QueuedEvictedmeans the oldest pending message was dropped, but this helper returns the sameGateOutcome::Queuedas a normal enqueue. That makes overflow-induced message loss invisible to the HTTP/API callers and any downstream eventing that currently only sees a successful queue.🤖 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 `@src/main.rs` around lines 4314 - 4339, The match arm for InboundDeliveryDispatch::QueuedEvicted must not map to the same GateOutcome::Queued as a normal enqueue; change it to return a distinct outcome (e.g., GateOutcome::QueuedEvicted or GateOutcome::Queued { evicted: true }) so callers can detect overflow and dropped messages; update the match arm handling InboundDeliveryDispatch::QueuedEvicted to return that new GateOutcome and include eviction metadata (dropped_from, queue_len) so state.accept_inbound and any HTTP/API or eventing paths that consume GateOutcome can surface/log the eviction to clients and downstream systems (adjust GateOutcome enum/consumers accordingly).
🧹 Nitpick comments (1)
src/listen_api.rs (1)
3205-3212: ⚡ Quick winAdd auth-coverage for the
/modeback-compat alias.The auth matrix covers
/delivery-modebut skips/api/spawned/{name}/mode, which is still intentionally supported. Add GET/PUT alias cases so this compatibility path is protected from regressions.Suggested test delta
for (method, path) in [ ("GET", "/api/spawned/worker-a/delivery-mode"), ("PUT", "/api/spawned/worker-a/delivery-mode"), + ("GET", "/api/spawned/worker-a/mode"), + ("PUT", "/api/spawned/worker-a/mode"), ("GET", "/api/spawned/worker-a/pending"), ("POST", "/api/spawned/worker-a/flush"), ] {🤖 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 `@src/listen_api.rs` around lines 3205 - 3212, The test inbound_delivery_routes_require_auth currently enumerates routes for auth checks but omits the backward-compatible alias paths /api/spawned/{name}/mode; update the route list inside inbound_delivery_routes_require_auth (where test_router is used) to also include the GET and PUT cases for "/api/spawned/worker-a/mode" so the legacy aliases are exercised (i.e., add ("GET", "/api/spawned/worker-a/mode") and ("PUT", "/api/spawned/worker-a/mode") to the array of (method, path) tuples).
🤖 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 `@src/cli/commands/drive.ts`:
- Around line 496-500: The restore-on-exit broker call to setInboundDeliveryMode
(invoked in the error and 'no_pty' paths and again around lines 673-675) is
awaited with no timeout, so a stalled network request can leave
runDriveSession() unresolved; update those call sites (the
setInboundDeliveryMode(...) invocations) to use a bounded request by adding an
AbortController/timeout or using a provided timeout-capable fetch helper (e.g.,
wrap deps.fetch with a timeout signal or call a timeoutFetch helper) and pass
the controller.signal into setInboundDeliveryMode so the restore is aborted
after a short deadline, ensuring detach/error paths cannot hang indefinitely.
In `@src/cli/commands/relay.ts`:
- Around line 320-325: The restore calls to setInboundDeliveryMode (invoked with
connection, name, previousMode ?? 'auto_inject', deps.fetch) can hang if broker
I/O stalls; wrap those calls in a time-bounded helper that races the original
Promise against a timeout (e.g., Promise.race with a timeout rejection or
resolved sentinel) and ensure the timeout path logs an error via deps.error and
returns/continues gracefully; replace the direct calls in the error branches
shown (the cases that call setInboundDeliveryMode around snapshot handling and
the later calls at the other restore location) with this timed wrapper so
teardown never waits indefinitely.
In `@src/main.rs`:
- Around line 1413-1419: Persist the delivery_states HashMap (type
HashMap<String, InboundDeliveryState>) together with pending_deliveries so
worker inbound delivery modes and queued manual_flush messages survive --persist
restarts: update the same save/load persistence routines that currently
serialize pending_deliveries to also include delivery_states, add
delivery_states to the persisted struct/schema, include it when writing and
restore it when reading, and ensure restored InboundDeliveryState entries are
inserted back into the delivery_states variable (leaving lazy-creation behavior
intact for missing entries).
---
Outside diff comments:
In `@src/main.rs`:
- Around line 4314-4339: The match arm for
InboundDeliveryDispatch::QueuedEvicted must not map to the same
GateOutcome::Queued as a normal enqueue; change it to return a distinct outcome
(e.g., GateOutcome::QueuedEvicted or GateOutcome::Queued { evicted: true }) so
callers can detect overflow and dropped messages; update the match arm handling
InboundDeliveryDispatch::QueuedEvicted to return that new GateOutcome and
include eviction metadata (dropped_from, queue_len) so state.accept_inbound and
any HTTP/API or eventing paths that consume GateOutcome can surface/log the
eviction to clients and downstream systems (adjust GateOutcome enum/consumers
accordingly).
---
Nitpick comments:
In `@src/listen_api.rs`:
- Around line 3205-3212: The test inbound_delivery_routes_require_auth currently
enumerates routes for auth checks but omits the backward-compatible alias paths
/api/spawned/{name}/mode; update the route list inside
inbound_delivery_routes_require_auth (where test_router is used) to also include
the GET and PUT cases for "/api/spawned/worker-a/mode" so the legacy aliases are
exercised (i.e., add ("GET", "/api/spawned/worker-a/mode") and ("PUT",
"/api/spawned/worker-a/mode") to the array of (method, path) tuples).
🪄 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 Plus
Run ID: b3977aba-3539-4b30-a7ae-6e569b66af7e
📒 Files selected for processing (19)
packages/sdk/src/__tests__/orchestration-upgrades.test.tspackages/sdk/src/client.tspackages/sdk/src/index.tspackages/sdk/src/protocol.tssrc/cli/bootstrap.test.tssrc/cli/bootstrap.tssrc/cli/commands/drive.test.tssrc/cli/commands/drive.tssrc/cli/commands/new.test.tssrc/cli/commands/new.tssrc/cli/commands/relay.test.tssrc/cli/commands/relay.tssrc/cli/lib/spawn-and-attach.tssrc/listen_api.rssrc/main.rssrc/types.rsweb/content/docs/reference-broker-api.mdxweb/content/docs/reference-cli.mdxweb/content/docs/typescript-sdk.mdx
| await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); | ||
| deps.error(`Error: ${snapshot.message ?? `no agent named '${name}'`}`); | ||
| return 1; | ||
| case 'no_pty': | ||
| await setSessionMode(connection, name, previousMode ?? 'passthrough', deps.fetch); | ||
| await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); |
There was a problem hiding this comment.
Bound restore-on-exit broker calls so detach/error paths can’t hang indefinitely.
Line 496, Line 500, and Line 673 currently wait on a network restore call with no timeout budget. If that request stalls, runDriveSession() can remain unresolved after detach/error.
💡 Suggested fix
+ const restoreModeBestEffort = async (): Promise<void> => {
+ const target = previousMode ?? 'auto_inject';
+ await Promise.race([
+ setInboundDeliveryMode(connection, name, target, deps.fetch).then(() => undefined),
+ new Promise<void>((resolve) => setTimeout(resolve, 1500)),
+ ]);
+ };
switch (snapshot.status) {
@@
case 'not_found':
- await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch);
+ await restoreModeBestEffort();
deps.error(`Error: ${snapshot.message ?? `no agent named '${name}'`}`);
return 1;
case 'no_pty':
- await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch);
+ await restoreModeBestEffort();
deps.error(`Error: ${snapshot.message ?? `agent '${name}' has no PTY to drive`}`);
return 1;
@@
- void setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch).finally(() => {
+ void restoreModeBestEffort().finally(() => {
resolve(code);
});Also applies to: 673-675
🤖 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 `@src/cli/commands/drive.ts` around lines 496 - 500, The restore-on-exit broker
call to setInboundDeliveryMode (invoked in the error and 'no_pty' paths and
again around lines 673-675) is awaited with no timeout, so a stalled network
request can leave runDriveSession() unresolved; update those call sites (the
setInboundDeliveryMode(...) invocations) to use a bounded request by adding an
AbortController/timeout or using a provided timeout-capable fetch helper (e.g.,
wrap deps.fetch with a timeout signal or call a timeoutFetch helper) and pass
the controller.signal into setInboundDeliveryMode so the restore is aborted
after a short deadline, ensuring detach/error paths cannot hang indefinitely.
| await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); | ||
| deps.error(`Error: ${snapshot.message ?? `no agent named '${name}'`}`); | ||
| return 1; | ||
| case 'no_pty': | ||
| await setSessionMode(connection, name, previousMode ?? 'passthrough', deps.fetch); | ||
| await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); | ||
| deps.error(`Error: ${snapshot.message ?? `agent '${name}' has no PTY to attach to`}`); |
There was a problem hiding this comment.
Avoid indefinite waits during relay teardown by time-bounding mode restore.
Line 320, Line 324, and Line 452 run restore calls without a timeout. If broker I/O hangs, detach/error completion can stall and never resolve the session promise.
💡 Suggested fix
+ const restoreModeBestEffort = async (): Promise<void> => {
+ const target = previousMode ?? 'auto_inject';
+ await Promise.race([
+ setInboundDeliveryMode(connection, name, target, deps.fetch).then(() => undefined),
+ new Promise<void>((resolve) => setTimeout(resolve, 1500)),
+ ]);
+ };
@@
case 'not_found':
- await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch);
+ await restoreModeBestEffort();
deps.error(`Error: ${snapshot.message ?? `no agent named '${name}'`}`);
return 1;
case 'no_pty':
- await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch);
+ await restoreModeBestEffort();
deps.error(`Error: ${snapshot.message ?? `agent '${name}' has no PTY to attach to`}`);
return 1;
@@
- void setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch).finally(() => {
+ void restoreModeBestEffort().finally(() => {
resolve(code);
});Also applies to: 452-454
🤖 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 `@src/cli/commands/relay.ts` around lines 320 - 325, The restore calls to
setInboundDeliveryMode (invoked with connection, name, previousMode ??
'auto_inject', deps.fetch) can hang if broker I/O stalls; wrap those calls in a
time-bounded helper that races the original Promise against a timeout (e.g.,
Promise.race with a timeout rejection or resolved sentinel) and ensure the
timeout path logs an error via deps.error and returns/continues gracefully;
replace the direct calls in the error branches shown (the cases that call
setInboundDeliveryMode around snapshot handling and the later calls at the other
restore location) with this timed wrapper so teardown never waits indefinitely.
| // Per-worker inbound-delivery-mode + pending-relay-message queue. Lives | ||
| // parallel to `workers.workers` so we can swap modes / inspect / | ||
| // drain without touching `WorkerHandle` (which holds OS-level | ||
| // process state). See `relay_broker::types::SessionState`. Entries | ||
| // process state). See `relay_broker::types::InboundDeliveryState`. Entries | ||
| // are created lazily on first lookup and removed wherever workers | ||
| // exit (`Release` arm, `worker_exited` frame, `reap_exited` sweep). | ||
| let mut session_states: HashMap<String, SessionState> = HashMap::new(); | ||
| let mut delivery_states: HashMap<String, InboundDeliveryState> = HashMap::new(); |
There was a problem hiding this comment.
Persist delivery_states alongside pending_deliveries.
delivery_states is now the only place that stores a worker's inbound delivery mode and its queued manual_flush messages, but this file still only saves/loads pending_deliveries. In --persist mode, a broker restart will silently reset workers back to the default mode and drop any queued inbound messages that were waiting for a flush.
🤖 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 `@src/main.rs` around lines 1413 - 1419, Persist the delivery_states HashMap
(type HashMap<String, InboundDeliveryState>) together with pending_deliveries so
worker inbound delivery modes and queued manual_flush messages survive --persist
restarts: update the same save/load persistence routines that currently
serialize pending_deliveries to also include delivery_states, add
delivery_states to the persisted struct/schema, include it when writing and
restore it when reading, and ensure restored InboundDeliveryState entries are
inserted back into the delivery_states variable (leaving lazy-creation behavior
intact for missing entries).
There was a problem hiding this comment.
No issues found across 19 files
You’re at about 96% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/listen_api.rs (1)
1204-1230:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winNormalize malformed
delivery-modebodies to the documented 400 response.Right now, only syntactically valid
{ "mode": "..." }payloads reachInboundDeliveryMode::parse(). Missing fields, wrong types, invalid JSON, or a missing JSON content type are rejected by Axum's Json extractor first, which returns 422 Unprocessable Entity by default—conflicting with the documentedinvalid_modecontract that promises a 400 response.Accept
Result<axum::Json<SetInboundDeliveryModePayload>, _>to interceptJsonRejectionand map those cases through the sameapi_error(..., "invalid_mode", ...)path. Add regression tests for{}and{ "mode": 1 }to lock in the fix.🤖 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 `@src/listen_api.rs` around lines 1204 - 1230, The handler listen_api_set_inbound_delivery_mode currently uses axum::Json extractor which yields 422 on malformed/missing fields; change the signature to accept Result<axum::Json<SetInboundDeliveryModePayload>, axum::extract::rejection::JsonRejection> so you can match the Err case and convert it into the same api_error(axum::http::StatusCode::BAD_REQUEST, "invalid_mode", ...) response used for invalid_mode, then proceed to call InboundDeliveryMode::parse on the Ok payload as before; also add regression tests that POST/PUT with {} and with { "mode": 1 } produce the documented 400 "invalid_mode" response to lock behavior.
🤖 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 `@web/content/docs/reference-broker-api.mdx`:
- Around line 467-469: The curl examples for the PUT
/api/spawned/{id}/delivery-mode endpoint are missing the required Content-Type:
application/json header, causing Axum's Json extractor to reject the request;
update the examples that call the PUT /delivery-mode (shown as curl -sX PUT
localhost:3888/api/spawned/Alice/delivery-mode ...) to include -H "Content-Type:
application/json" (and keep the existing X-API-Key header) so the JSON body is
accepted by the handler.
---
Outside diff comments:
In `@src/listen_api.rs`:
- Around line 1204-1230: The handler listen_api_set_inbound_delivery_mode
currently uses axum::Json extractor which yields 422 on malformed/missing
fields; change the signature to accept
Result<axum::Json<SetInboundDeliveryModePayload>,
axum::extract::rejection::JsonRejection> so you can match the Err case and
convert it into the same api_error(axum::http::StatusCode::BAD_REQUEST,
"invalid_mode", ...) response used for invalid_mode, then proceed to call
InboundDeliveryMode::parse on the Ok payload as before; also add regression
tests that POST/PUT with {} and with { "mode": 1 } produce the documented 400
"invalid_mode" response to lock behavior.
🪄 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 Plus
Run ID: d7968b2f-d335-407f-8aae-046f2c6e6243
📒 Files selected for processing (2)
src/listen_api.rsweb/content/docs/reference-broker-api.mdx
| curl -sX PUT localhost:3888/api/spawned/Alice/delivery-mode \ | ||
| -H "X-API-Key: $KEY" \ | ||
| -d '{"mode":"human"}' | ||
| -d '{"mode":"manual_flush"}' |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In axum 0.8, does the Jsonextractor requireContent-Type: application/json, and what response status does it return when that header is missing?
💡 Result:
In Axum 0.8, the Json extractor does require the request to have a Content-Type: application/json (or compatible) header [1][2]. When this header is missing, the extractor rejects the request with a JsonRejection::MissingJsonContentType error [3][4]. By default, this rejection is converted into an HTTP 400 Bad Request response [3][5]. You can customize this behavior by handling the JsonRejection explicitly in your handler or by using middleware to map the rejection to a different status code if desired [3][6].
Citations:
- 1: https://docs.rs/crate/axum/0.8.7/source/src/json.rs
- 2: https://docs.rs/axum/latest/axum/struct.Json.html
- 3: https://docs.rs/axum/0.8.8/axum/extract/index.html
- 4: https://docs.rs/axum/latest/axum/extract/rejection/struct.MissingJsonContentType.html
- 5: https://docs.rs/axum/latest/axum/extract/index.html
- 6: Stop Axum’s JSON extractor from reporting errors that give hackers clues to what they're doing wrong tokio-rs/axum#3301
🏁 Script executed:
# Find and examine the listen_api.rs handler for delivery-mode
find . -name "listen_api.rs" -type fRepository: AgentWorkforce/relay
Length of output: 84
🏁 Script executed:
# Read the documentation file around the affected lines
cat -n web/content/docs/reference-broker-api.mdx | sed -n '460,500p'Repository: AgentWorkforce/relay
Length of output: 1593
🏁 Script executed:
# Search for the delivery-mode handler in listen_api.rs
rg -A 10 "delivery-mode" src/listen_api.rsRepository: AgentWorkforce/relay
Length of output: 6305
Add Content-Type: application/json to the new PUT /delivery-mode examples.
These two curl examples send JSON with -d, but the handler uses Axum's Json extractor, which requires the Content-Type header. Without it, requests fail with HTTP 400 Bad Request before reaching the route logic. The test suite confirms this requirement by explicitly setting the header on all PUT requests to this endpoint.
Suggested doc fix
curl -sX PUT localhost:3888/api/spawned/Alice/delivery-mode \
-H "X-API-Key: $KEY" \
+ -H "Content-Type: application/json" \
-d '{"mode":"manual_flush"}'
@@
curl -sX PUT localhost:3888/api/spawned/Alice/delivery-mode \
-H "X-API-Key: $KEY" \
+ -H "Content-Type: application/json" \
-d '{"mode":"auto_inject"}'Also applies to: 490-492
🤖 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 `@web/content/docs/reference-broker-api.mdx` around lines 467 - 469, The curl
examples for the PUT /api/spawned/{id}/delivery-mode endpoint are missing the
required Content-Type: application/json header, causing Axum's Json extractor to
reject the request; update the examples that call the PUT /delivery-mode (shown
as curl -sX PUT localhost:3888/api/spawned/Alice/delivery-mode ...) to include
-H "Content-Type: application/json" (and keep the existing X-API-Key header) so
the JSON body is accepted by the handler.
Summary
human/passthroughtomanual_flush/auto_inject/modecompatibility routeview,drive, andpassthrough; remove theagent-relay relayattach alias/commandTests
cargo testcargo test types::inbound_delivery_tests./node_modules/.bin/vitest run src/cli/bootstrap.test.ts src/cli/commands/drive.test.ts src/cli/commands/passthrough.test.ts src/cli/commands/new.test.ts./node_modules/.bin/vitest run src/cli/commands/passthrough.test.ts(cd packages/sdk && ../../node_modules/.bin/vitest run src/__tests__/orchestration-upgrades.test.ts -t "exposes inbound delivery mode")git diff HEAD --checknpm run buildNote: the full SDK orchestration-upgrades file was not used as the verification gate because unfiltered tests try to reach
api.relaycast.devand fail under the sandboxed network.