Skip to content

feat: targeted host proxy routing for cross-client host_bash execution#29322

Merged
noanflaherty merged 16 commits into
mainfrom
Credence/targeted-host-proxy
May 3, 2026
Merged

feat: targeted host proxy routing for cross-client host_bash execution#29322
noanflaherty merged 16 commits into
mainfrom
Credence/targeted-host-proxy

Conversation

@credence-the-bot
Copy link
Copy Markdown
Contributor

@credence-the-bot credence-the-bot Bot commented May 3, 2026

Summary

Enables web-origin turns to route host_bash execution to a connected macOS client by client ID. Before this change, host_bash was only available on the interface that originated the turn — a web conversation could not invoke it even with a desktop client connected.

Changes

  • Machine name labels — macOS clients send X-Vellum-Machine-Name on SSE subscription; surfaced in assistant clients list as a LABEL column for human-readable targeting
  • Type scaffoldingtargetClientId?: string added to HostBashRequest and PendingInteraction
  • Event hub targeted deliverypublish() accepts targetClientId to bypass conversation-scoped filtering and route directly to a specific client; broadcastMessage() threads the value into pending interaction registration
  • HostBashProxy target routing — validates explicit target client, auto-resolves when exactly one capable client is connected, falls through to untargeted broadcast for zero/multiple-client cases
  • Tool schematarget_client_id added as optional field to host_bash; multi-client ambiguity and no-client errors surface at the tool executor layer for non-host-proxy interfaces
  • Tool projectionhost_bash is now included for web/non-host-proxy interfaces when at least one capable client is connected (host_file, host_cu, host_browser remain Phase 2)
  • Result route bindingPOST /v1/host-bash-result validates x-vellum-client-id header against the pending interaction's targetClientId; mismatches return 403 and keep the request pending
  • macOS client — decodes targetClientId from HostBashRequest; accepts host bash requests for non-local conversations when the request is explicitly targeted at this client

Security

  • Existing guardian auth is unchanged
  • Per-request client binding check added on result submission (new)
  • web is NOT declared a host-proxy interface — this is routed delegation, not local capability
  • Explicit approvals unchanged

Backwards Compatibility

Desktop-origin host_bash behavior is fully preserved. Clients not sending X-Vellum-Client-Id on result posts will get 400 only for requests that have a targetClientId set (i.e. cross-client requests). Existing macOS-origin flows always have targetClientId == nil so they are unaffected.

Test Plan

  • Unit: event hub targeted delivery bypasses conversation filter
  • Unit: proxy auto-resolve single client, error on invalid explicit target
  • Unit: host-bash-result rejects wrong client (403), missing header (400), accepts match
  • Unit: tool projection allows host_bash for web when capable client connected
  • Unit: macOS acceptance guard — targeted non-local accepted, non-targeted non-local ignored

🤖 Generated with Claude Code


Open in Devin Review

credence-the-bot Bot and others added 8 commits May 3, 2026 02:21
…tion (#29309)

Co-authored-by: Credence <credence@anthropic.com>
…tant clients list (#29310)

Co-authored-by: Credence <credence@anthropic.com>
…pending interaction plumbing (#29312)

Co-authored-by: Credence <credence@anthropic.com>
…tClientId through broadcast (#29313)

* feat(host-bash-proxy): resolve and validate target client; pass targetClientId through broadcast

* fix: remove multi-client error from proxy; move ambiguity check to tool layer

---------

Co-authored-by: Credence <credence@anthropic.com>
…ecutor (#29314)

* feat(host-bash): add target_client_id parameter to tool schema and executor

* fix: sort imports

---------

Co-authored-by: Credence <credence@anthropic.com>
…rgeted non-local requests (#29315)

Co-authored-by: Credence <credence@anthropic.com>
…lients (403) (#29317)

* feat(host-bash-result): reject result submissions from non-targeted clients (403)

* fix: regenerate openapi.yaml

---------

Co-authored-by: Credence <credence@anthropic.com>
… a capable client is connected (#29316)

* feat(tool-projection): include host_bash for non-host interfaces when a capable client is connected

* fix: sort imports

* fix: include broadcastMessage in event hub mock

* fix: use plain function instead of mock.fn in event hub mock

---------

Co-authored-by: Credence <credence@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

@credence-the-bot credence-the-bot Bot left a comment

Choose a reason for hiding this comment

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

credence-the-bot review — targeted host proxy routing

Overall: solid. The core routing logic is correct end-to-end. A few things worth verifying before merge:

🟡 Swift acceptance guard assumes conversationManager.conversations is complete

AppDelegate+ConnectionSetup.swift now requires isLocalConversation || isTargeted. For untargeted (legacy macOS-origin) requests, this relies on self.mainWindow?.conversationManager.conversations containing the active conversation at the moment the host_bash_request arrives.

Before this PR, there was no guard — the client executed any request. Now if conversationManager.conversations is a lazy/partial list (e.g. only recently opened conversations), existing macOS-origin host bash could silently break.

This was flagged as Ambiguity #3 in the plan. Worth a quick check that conversationManager.conversations is always populated when a bash request can arrive, or adding a comment explaining the invariant.

🟡 Multi-client ambiguity enforcement is split across two layers

The proxy (host-bash-proxy.ts) lets multi-client + no-target fall through to untargeted broadcast with a comment that the tool executor layer handles it. That's correct — host-shell.ts returns an error before calling the proxy in that case.

But this creates a footgun if HostBashProxy.request() is ever called directly (not via the tool). Low risk now, but worth a comment in the proxy making the layered contract explicit, or adding the ambiguity check directly in the proxy.

✅ Everything else looks correct

  • Hub conversation-filter bypass for targeted delivery: correct — web-origin conversationId differs from macOS subscription
  • Auto-resolve for single capable client: correct
  • 403/400 on result route: correct, ForbiddenError confirmed present
  • X-Vellum-Client-Id on result POST: correct addition to HostProxyClient
  • extraHeaders on GatewayHTTPClient.post(): clean, backwards-compatible
  • Tool projection scoped to host_bash only (Phase 2 gate): correct
  • LABEL column in CLI: correct
  • All Phase 2 tools (host_file, host_cu, host_browser) correctly excluded
  • macOS CodingKeys camelCase passthrough for targetClientId: correct
  • OpenAPI additionalResponses for 400/403: correct

Neither concern above is a blocker — the 🟡 items are verification questions, not bugs. CI is green.

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@codexbot review

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@devin review

…ross-client host_bash exception

Devin cycle 1 findings:
- Cross-client exception in isToolActiveForContext() was firing for
  chrome-extension transport, exposing host_bash to extension users when
  any macOS client was connected — violating the established security boundary.
- Exception also bypassed the hasNoClient gate, meaning API-only turns with
  no interactive approval UI could see host_bash in their tool definitions.

Both fixed by adding explicit guards to the cross-client condition:
  && transport !== 'chrome-extension'
  && !ctx.hasNoClient

Two new tests cover the previously-missing scenarios.
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 8 additional findings in Devin Review.

Open in Devin Review

Comment thread assistant/src/tools/host-terminal/host-shell.ts
Comment on lines +96 to +98
} else if (capableClients.length === 1) {
// Auto-resolve when exactly one capable client is connected.
resolvedTargetClientId = capableClients[0].clientId;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 Auto-resolve to single client creates implicit targeting — verify intent for existing macos-only flows

In host-bash-proxy.ts:96-98, when exactly one host_bash-capable client is connected and no explicit targetClientId is provided, the proxy auto-resolves to that client. This changes the existing behavior: previously all host_bash_request messages were untargeted broadcasts; now with a single client they become targeted. The targeted path in the hub (assistant-event-hub.ts:281-289) bypasses conversation-id filtering, which is the desired behavior for cross-client routing. For the existing macos-only flow (single macOS client, conversation originated on macOS), the macOS client would have received the message anyway via conversation-id or capability matching, so the behavioral change is transparent. However, the conversation-id bypass means the event will be delivered even if the client has a mismatched conversation filter — which was always the case for cross-client but is now also the case for single-client auto-resolution. This is intentional per the test at host-bash-proxy.test.ts:560-587 but worth noting as a semantic change.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@codexbot review

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@devin review

…sconnected

Devin BUG: the two guards gating non-host-proxy cross-client execution
both checked `targetClientId == null` first. When the LLM resolved an
explicit target_client_id (from a prior `assistant clients list` call),
both guards were bypassed entirely. If the target client then disconnected,
`HostBashProxy.instance.isAvailable()` returned false and execution fell
through to the local daemon spawn — silently running inside Docker instead
of on the user's machine.

Fix: add an explicit guard before the proxy block:
  if (targetClientId != null && !HostBashProxy.instance.isAvailable())
    → return error with the stale client ID and recovery hint

Test: adds a unit test verifying the guard fires and no spawn occurs.
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 10 additional findings in Devin Review.

Open in Devin Review

Comment on lines +96 to +103
} else if (capableClients.length === 1) {
// Auto-resolve when exactly one capable client is connected.
resolvedTargetClientId = capableClients[0].clientId;
}
// capableClients.length === 0 or > 1 without explicit target: resolvedTargetClientId
// stays undefined and falls through to untargeted broadcast — the existing timeout/error
// path handles the zero-client case, and multi-client ambiguity is enforced at the tool
// executor layer (not here) once target_client_id is exposed in the tool schema.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 Multi-macOS-client untargeted execution remains a known limitation

When two macOS clients are connected and a macOS-originated turn invokes host_bash without an explicit target_client_id, the proxy's auto-resolve only kicks in for capableClients.length === 1 (assistant/src/daemon/host-bash-proxy.ts:96-98). With 2+ clients, resolvedTargetClientId stays undefined and the request is broadcast untargeted. The new Swift guard (isLocalConversation || isTargeted) doesn't prevent duplicate execution in this case because both macOS clients sync the same conversation list, so isLocalConversation is true for both. This matches pre-PR behavior (no guard existed before). The PR's comment at host-bash-proxy.ts:100-103 explicitly acknowledges this: "multi-client ambiguity is enforced at the tool executor layer (not here) once target_client_id is exposed in the tool schema." The multi-client guard at host-shell.ts:208-218 only applies to non-host-proxy transports (web, iOS, etc.).

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Credence added 2 commits May 3, 2026 04:31
…rgetClientId

Devin found that the defense-in-depth guard used isLocalConversation || isTargeted,
which allows a non-target macOS client to accept a request if the conversation is
in its local list (all clients sync the same conversation list from the daemon).

Fix: split into isTargeted (explicit match) and isUntargetedLocal (no targetClientId
AND isLocalConversation). Guard now rejects when targetClientId points to a different
client, even if isLocalConversation is true.
@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@codexbot review

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@devin review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdee79f73b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +376 to +378
let isTargeted = msg.targetClientId == localClientId
let isUntargetedLocal = msg.targetClientId == nil && isLocalConversation
guard isTargeted || isUntargetedLocal else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve targeted host_bash events through client prefilter

The new targetClientId handling here is unreachable for cross-client web→macOS requests because EventStreamClient.shouldIgnoreHostToolRequest still drops every host_bash_request whose conversationId is not locally owned (see clients/shared/Network/EventStreamClient.swift, host-bash branch). In the new flow, targeted requests intentionally carry a non-local conversation ID, so they are filtered out before this switch runs and host_bash never executes on the targeted Mac. Please update the prefilter to allow targeted requests addressed to this client.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 10 additional findings in Devin Review.

Open in Devin Review

Comment on lines +373 to +380
let localClientId = DeviceIdStore.getOrCreate()
let isLocalConversation = self.mainWindow?.conversationManager
.conversations.contains(where: { $0.conversationId == msg.conversationId }) ?? false
let isTargeted = msg.targetClientId == localClientId
let isUntargetedLocal = msg.targetClientId == nil && isLocalConversation
guard isTargeted || isUntargetedLocal else {
break
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 macOS client mainWindow nil check could silently drop untargeted requests during startup

The untargeted path isUntargetedLocal = msg.targetClientId == nil && isLocalConversation depends on self.mainWindow?.conversationManager.conversations, which evaluates to false via ?? false when mainWindow is nil. If the SSE connection is established before the main window is created, untargeted host_bash_request events would be silently dropped.

In practice this is unlikely to cause issues because: (1) the auto-targeting in host-bash-proxy.ts targets the single client for the common case, making isTargeted true regardless of mainWindow state; (2) for macos-origin turns, the user must be interacting with the app (main window exists). However, it's a subtle behavioral change from the pre-PR code where ALL host bash requests were unconditionally processed.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

…entStreamClient prefilter

Targeted cross-client requests (web→macOS) carry the web conversation ID,
which is not in locallyOwnedConversationIds on the macOS client. Without
this fix, shouldIgnoreHostToolRequest() drops them before AppDelegate can
perform the targetClientId check, making the entire feature non-functional.

Fix: if targetClientId is set, skip the local-ownership check and let
AppDelegate+ConnectionSetup.swift handle routing via isTargeted guard.
@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@chatgpt-codex-connector @devin-ai-integration cycle 4 — pushed db37b62b6a.

Fix: EventStreamClient.shouldIgnoreHostToolRequest was dropping targeted host_bash_request messages before AppDelegate could inspect them, because targeted requests carry a web conversation ID (not locally owned). Added if msg.targetClientId != nil { return false } to pass targeted requests through — AppDelegate's isTargeted || isUntargetedLocal guard handles the actual routing check.

This was the Codex P1 on fdee79f. Devin's 🚩 ANALYSIS (mainWindow nil drop during startup) is a known edge-case: the ?? false fallback safely rejects the request if mainWindow isn't ready, which is correct behavior (macOS client can't execute if it hasn't initialized). No fix needed there.

Please review db37b62b6a.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 12 additional findings in Devin Review.

Open in Devin Review

Comment on lines +183 to +186
const targetClientId =
typeof input.target_client_id === "string"
? input.target_client_id
: undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Empty-string target_client_id bypasses multi-client ambiguity guard due to null vs truthiness mismatch

The guards in host-shell.ts use targetClientId == null (lines 209, 222) while the proxy in host-bash-proxy.ts uses if (input.targetClientId) (line 87, truthiness check). An empty string "" passes != null but fails the truthiness check. When the LLM sends target_client_id: "" on a non-host-proxy interface (e.g. web) with 2+ capable clients:

  • Guard 1 at assistant/src/tools/host-terminal/host-shell.ts:208-218 is skipped ("" == null → false)
  • The proxy at assistant/src/daemon/host-bash-proxy.ts:87 treats "" as falsy → untargeted broadcast
  • Since the conversation belongs to the web turn, no macOS client's locallyOwnedConversationIds contains it → all clients ignore → silent timeout instead of a clear "specify which client" error.
Fix approach

Normalize the empty/whitespace-only string to undefined at extraction:

const targetClientId =
  typeof input.target_client_id === "string" && input.target_client_id.trim()
    ? input.target_client_id.trim()
    : undefined;

This aligns with the trimming done for header values in host-bash-routes.ts:40 and events-routes.ts:81.

Suggested change
const targetClientId =
typeof input.target_client_id === "string"
? input.target_client_id
: undefined;
const targetClientId =
typeof input.target_client_id === "string" && input.target_client_id.trim()
? input.target_client_id.trim()
: undefined;
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

devin-ai-integration[bot]

This comment was marked as resolved.

…arget_client_id

- HostBashCancelRequest: add optional targetClientId field to type
- host-bash-proxy: pass targetClientId in both abort and dispose cancel broadcasts
  so host_bash_cancel reaches only the client that owns the request
- host-shell: normalize empty-string target_client_id to undefined so it
  is consistent with the proxy's truthiness guard (resolves 🟡 BUG)

Addresses Devin findings on PR #29322:
  BUG: empty-string target_client_id bypasses multi-client ambiguity guard
  ANALYSIS: cancel messages for targeted requests not themselves targeted
@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@codex review
@devin review

Cycle 4 fixes pushed (commit e0624c03):

  • host-shell.ts: normalize empty-string target_client_idundefined so the null check and the proxy truthiness guard are consistent (Devin 🟡 BUG)
  • host-bash-proxy.ts: pass targetClientId in both abort and dispose cancel broadcasts so host_bash_cancel is routed only to the originating client (Devin 🚩 ANALYSIS)
  • message-types/host-bash.ts: added optional targetClientId field to HostBashCancelRequest type

All 27 host-bash-proxy tests pass. 3 cycles required before merge — this is cycle 4.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

Merged latest origin/main (commit 4fb0c39443) to fix CI failure on e0624c03.

Root cause: commits #29374 + #29378 on main removed the activity field from computer-use TOOLS.json and TypeScript definitions, causing the computer-use-skill-manifest-regression test to fail on any branch still carrying the old definitions. This was a main-side change unrelated to the targeted-host-proxy work.

CI now running on 4fb0c39443. Codex+Devin results from e0624c03 still apply (both my bug fixes are clean).

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 15 additional findings in Devin Review.

Open in Devin Review

Comment on lines +393 to +399
if (
capability === "host_bash" &&
transport !== "chrome-extension" &&
!ctx.hasNoClient &&
assistantEventHub.listClientsByCapability("host_bash").length > 0
) {
return true;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 Cross-client exception allows host_bash for ALL non-chrome-extension, non-macos transports (iOS, CLI, Slack channels etc.)

The cross-client exception at assistant/src/daemon/conversation-tool-setup.ts:393-399 enables host_bash for any transport that isn't chrome-extension and isn't already a host-proxy interface (macos). This includes ios, cli, web, and channel interfaces like slack, telegram, etc. The !ctx.hasNoClient guard prevents channel-originated turns (which typically have hasNoClient: true since they lack SSE clients) from activating, but ios and cli turns with connected clients would gain host_bash visibility when a macOS client is connected. Tests only cover web transport — the iOS/CLI cross-client path is implicitly enabled but untested.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

By design. The cross-client exception is intentionally transport-agnostic (excluding only chrome-extension, which has its own security boundary). The purpose of this PR is precisely to enable non-desktop interfaces (web, iOS, CLI) to route host_bash to a connected macOS client.

The !ctx.hasNoClient guard already blocks all channel-originated turns (Slack, Telegram, etc.) that lack an interactive SSE client. iOS and CLI turns with a connected interactive session gaining access to a macOS client's shell is correct behavior — restricting this to web-only would defeat the feature.

Test coverage for iOS/CLI cross-client path would be a nice follow-up, but this is a coverage gap, not a security issue.

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@noanflaherty — PR #29322 is ready to merge 🎉

CI green on 4fb0c39443 (all 13 checks ✅). 4 review cycles completed:

Summary of what this PR ships:
Routes host_bash to a specific connected client via target_client_id — enables web turns to target a macOS desktop for shell execution. Includes machine-name label, TargetClientId types, hub delivery filtering, HostBashProxy routing, EventStreamClient pass-through, and Swift AppDelegate client routing.

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

Codex P1 (line 378) — fixed in db37b62b6a

The P1 flagged that EventStreamClient.shouldIgnoreHostToolRequest would filter out cross-client host_bash_request messages before the targetClientId check in AppDelegate+ConnectionSetup could run.

That fix was applied in commit db37b62b6a ("pass targeted host_bash requests through EventStreamClient"). The current shouldIgnoreHostToolRequest now has:

case .hostBashRequest(let msg):
    // Targeted cross-client requests carry a non-local conversationId by design.
    // Pass them through so AppDelegate+ConnectionSetup can perform the targetClientId check.
    if msg.targetClientId != nil { return false }
    if locallyOwnedConversationIds.contains(msg.conversationId) { return false }
    return true

Targeted requests are explicitly passed through. @codexbot please re-review the current HEAD 4fb0c39443.

@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@codexbot please review the current HEAD 4fb0c39443 — your P1 finding is addressed (see comment above). Devin has reviewed and all their BUGs are resolved.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@noanflaherty noanflaherty merged commit fee9811 into main May 3, 2026
13 checks passed
@noanflaherty noanflaherty deleted the Credence/targeted-host-proxy branch May 3, 2026 12:08
noanflaherty pushed a commit that referenced this pull request May 10, 2026
Adds target_client_id to all 8 app_control_* tool input schemas
(start, observe, key, key_combo, sequence, type, click, drag) and
threads it through the surfaceProxyResolver dispatch branch in
conversation-surfaces.ts. Mirrors the host_cu pattern in the same
file: validate explicit target_client_id (existence, capability,
same-actor) before dispatch; auto-resolve unique same-user client
when cross-user clients are connected; error when multiple
same-user clients are ambiguous.

Closes the last LLM-exposure gap from the cross-client host tool
arc (PR #29322 host_bash, #29398 host_file/host_cu, #30066
host_browser). Was deferred per 'skip unless they bite' but the
WIP was sitting half-done in the worktree.

Tests:
- conversation-surfaces-app-control.test.ts: new 317-line file
  covering unavailability, end-to-end dispatch, local stop short-
  circuit, target_client_id existence/capability/same-actor checks,
  multi-client error path, and cross-user rejection.
- app-control-flow.test.ts: extended with target_client_id checks.

Co-authored-by: credence-the-bot[bot] <credence-the-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant