Skip to content

refactor: migrate Swift HTTPTransport and CLI from /v1/runs to /v1/messages#8410

Merged
awlevin merged 1 commit into
mainfrom
remove-runs/pr4
Feb 24, 2026
Merged

refactor: migrate Swift HTTPTransport and CLI from /v1/runs to /v1/messages#8410
awlevin merged 1 commit into
mainfrom
remove-runs/pr4

Conversation

@awlevin
Copy link
Copy Markdown
Contributor

@awlevin awlevin commented Feb 24, 2026

PR 4/6: remove-runs plan. Migrates Swift and CLI clients to new message/approval endpoints.

Summary

  • Swift HTTPTransport: Changed createRun() (POST /v1/runs) to sendMessage() (POST /v1/messages). Updated sendDecision() to POST /v1/confirm with { requestId, decision }. Updated sendSecret() to POST /v1/secret with { requestId, value }. Removed activeRunId property and all run-related state tracking.

  • CLI: Replaced createRun() with sendMessage() (POST /v1/messages). Replaced submitDecision() with POST /v1/confirm. Replaced addTrustRule() with POST /v1/trust-rules. Removed getRun() status polling and all run-related types (CreateRunResponse, GetRunResponse). Added polling via new GET /v1/pending-interactions endpoint for approval discovery.

  • Server: Added GET /v1/pending-interactions?conversationKey=... endpoint so polling-based clients (CLI) can discover pending confirmations/secrets without SSE.

Test plan

  • CLI: send a message, verify it reaches the assistant via POST /v1/messages
  • CLI: trigger a tool approval, verify the confirmation prompt appears via pending-interactions polling
  • CLI: submit allow/deny decisions, verify they resolve via POST /v1/confirm
  • CLI: test trust rule allowlist/denylist flow via POST /v1/trust-rules
  • Swift: send a message via HTTP transport, verify it hits POST /v1/messages
  • Swift: approve a tool confirmation, verify it hits POST /v1/confirm
  • Swift: provide a secret, verify it hits POST /v1/secret
  • Verify no remaining references to /v1/runs in Swift or CLI code
  • CLI type-check: cd cli && bunx tsc --noEmit passes
  • Swift build: cd clients/macos && ./build.sh succeeds

Open with Devin

…ssages

Co-Authored-By: Claude <noreply@anthropic.com>
@awlevin awlevin self-assigned this Feb 24, 2026
@awlevin
Copy link
Copy Markdown
Contributor Author

awlevin commented Feb 24, 2026

👀 Where to focus your review

  • assistant/src/runtime/routes/approval-routes.ts (new handleListPendingInteractions endpoint): This is a new server-side endpoint added to support CLI polling. It was not in the original plan (PR 4 was scoped as client-only), but was necessary because the CLI is polling-based and has no SSE support. The endpoint returns the first pending confirmation/secret for a conversation — verify the response shape matches what the CLI expects.

  • cli/src/components/DefaultMainScreen.tsx (polling loop rewrite): The old loop used createRun() + getRun() status polling (queued/running/needs_confirmation/completed/failed). The new loop uses sendMessage() (fire-and-forget 202) + pollPendingInteractions() + pollMessages(). The completion detection now relies on seeing a new assistant message in the message poll, rather than a run status transition. This means the CLI will keep polling until an assistant message appears — verify this doesn't cause issues with multi-tool-call turns that may have many intermediate steps before a final message.

Risk level: Medium — the CLI polling model changed fundamentally from run-status-driven to message-poll-driven, which could affect perceived responsiveness and completion detection in edge cases.

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 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +232 to +236
pendingSecret: secret
? {
requestId: secret.requestId,
}
: null,
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.

🔴 Server returns incomplete pending-secret data, causing CLI to display 'undefined' fields

When the CLI polls GET /v1/pending-interactions, the server only returns { requestId } for pending secrets (approval-routes.ts:232-235), but the CLI's PendingSecret interface requires service, field, and label as mandatory string properties. The PendingInteraction stored server-side for secrets (registered at assistant/src/runtime/routes/conversation-routes.ts:176-180) doesn't capture any secret metadata — only kind: 'secret', session, and conversationId.

When the CLI receives this incomplete response and calls handleSecretPromptFn at line 1108, it accesses secret.label, secret.service, and secret.field, all of which are undefined at runtime.

Root Cause and Impact

The server-side PendingInteraction type for secrets doesn't store the secret metadata (service, field, label, description, placeholder, purpose, allowOneTimeSend). The original secret_request IPC message (assistant/src/daemon/ipc-contract/messages.ts:117-127) has all these fields, but they're discarded during registration at conversation-routes.ts:176-180.

As a result, the CLI's secret prompt displays:

  • "Secret needed: undefined" (line 1108: secret.label)
  • "Service: undefined / undefined" (line 1109: secret.service / secret.field)
  • The showSecretInput call (line 1131) passes undefined as the label
  • The allowOneTimeSend delivery selection (line 1119) never triggers since the field is missing

Impact: The entire secret prompt UX in the CLI is broken — users see nonsensical "undefined" labels and have no context about what secret is being requested.

Prompt for agents
Two changes are needed to fix the missing secret metadata:

1. In assistant/src/runtime/pending-interactions.ts, add a SecretDetails interface (similar to ConfirmationDetails) with fields: service, field, label, description?, placeholder?, purpose?, allowOneTimeSend?. Add an optional secretDetails property to the PendingInteraction interface.

2. In assistant/src/runtime/routes/conversation-routes.ts around line 176, when registering a secret interaction, capture the secret metadata from the msg object:
   pendingInteractions.register(msg.requestId, {
     session,
     conversationId,
     kind: 'secret',
     secretDetails: {
       service: msg.service,
       field: msg.field,
       label: msg.label,
       description: msg.description,
       placeholder: msg.placeholder,
       purpose: msg.purpose,
       allowOneTimeSend: msg.allowOneTimeSend,
     },
   });

3. In assistant/src/runtime/routes/approval-routes.ts lines 232-236, return the secret details in the response:
   pendingSecret: secret
     ? {
         requestId: secret.requestId,
         service: secret.secretDetails?.service,
         field: secret.secretDetails?.field,
         label: secret.secretDetails?.label,
         description: secret.secretDetails?.description,
         placeholder: secret.secretDetails?.placeholder,
         purpose: secret.secretDetails?.purpose,
         allowOneTimeSend: secret.secretDetails?.allowOneTimeSend,
       }
     : null,
Open in Devin Review

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

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: 1cfaa03d8c

ℹ️ 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 +233 to +235
? {
requestId: secret.requestId,
}
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 Include secret metadata in pending-interactions payload

The new polling response only returns requestId for pendingSecret, but the CLI secret flow expects fields like label, service, and field (and ultimately passes label into UI rendering). With this shape, a secret_request discovered via GET /pending-interactions produces undefined prompt data and can break the prompt UX for polling clients instead of presenting a usable secret request.

Useful? React with 👍 / 👎.

Comment on lines 1521 to +1525
h.addMessage(msg);
chatLogRef.current.push({ role: "assistant", content: msg.content });
if (!runId) {
h.setBusy(false);
h.hideSpinner();
return;
}
h.setBusy(false);
h.hideSpinner();
return;
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 Correlate completion to the submitted turn before returning

This loop now exits on the first unseen assistant message, regardless of which user turn it belongs to. If /v1/messages accepts this input while the conversation is already processing another turn (queue-if-busy), the CLI will treat the earlier turn's assistant reply as completion, clear busy state, and return before the current request is answered.

Useful? React with 👍 / 👎.

@awlevin awlevin merged commit 698b7b2 into main Feb 24, 2026
4 checks passed
@awlevin awlevin deleted the remove-runs/pr4 branch February 24, 2026 23:51
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