From a882b1548027deaea26e5de5b5f6b6a7de5b6c67 Mon Sep 17 00:00:00 2001 From: Noa Flaherty Date: Mon, 6 Apr 2026 12:05:51 -0400 Subject: [PATCH] refactor: discriminated union for transport metadata, remove iOS proxy setup --- .../src/daemon/handlers/conversations.ts | 11 +++----- .../src/daemon/message-types/conversations.ts | 27 ++++++++++++++++--- assistant/src/daemon/server.ts | 2 +- .../src/runtime/routes/conversation-routes.ts | 10 +++---- .../Network/Generated/GeneratedAPITypes.swift | 8 +++++- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/assistant/src/daemon/handlers/conversations.ts b/assistant/src/daemon/handlers/conversations.ts index d732ea4f27b..39e23c637f2 100644 --- a/assistant/src/daemon/handlers/conversations.ts +++ b/assistant/src/daemon/handlers/conversations.ts @@ -103,13 +103,10 @@ export function makeEventSender(params: { guardianPrincipalId: trustContext?.guardianPrincipalId ?? undefined, toolName: event.toolName, commandPreview: - redactSecrets( - summarizeToolInput(event.toolName, inputRecord), - ) || undefined, + redactSecrets(summarizeToolInput(event.toolName, inputRecord)) || + undefined, riskLevel: event.riskLevel, - activityText: activityRaw - ? redactSecrets(activityRaw) - : undefined, + activityText: activityRaw ? redactSecrets(activityRaw) : undefined, executionTarget: event.executionTarget, status: "pending", requestCode: generateCanonicalRequestCode(), @@ -304,7 +301,7 @@ export async function handleConversationCreate( // Only create the host bash proxy for desktop client interfaces that can // execute commands on the user's machine. Set before updateClient so // updateClient's call to hostBashProxy.updateSender targets the new proxy. - if (transportInterface === "macos" || transportInterface === "ios") { + if (transportInterface === "macos") { const proxy = new HostBashProxy(sendEvent, (requestId) => { pendingInteractions.resolve(requestId); }); diff --git a/assistant/src/daemon/message-types/conversations.ts b/assistant/src/daemon/message-types/conversations.ts index 78ac08f3d62..7f6319743ee 100644 --- a/assistant/src/daemon/message-types/conversations.ts +++ b/assistant/src/daemon/message-types/conversations.ts @@ -14,12 +14,10 @@ export interface ConversationListRequest { limit?: number; } -/** Lightweight conversation transport metadata for channel identity and natural-language guidance. */ -export interface ConversationTransportMetadata { +/** Shared fields for all transport metadata variants. */ +interface BaseTransportMetadata { /** Logical channel identifier (e.g. "desktop", "telegram", "mobile"). */ channelId: ChannelId; - /** Interface identifier for this transport (e.g. "macos", "ios", "cli"). */ - interfaceId?: InterfaceId; /** Optional natural-language hints for channel-specific UX behavior. */ hints?: string[]; /** Optional concise UX brief for this channel. */ @@ -28,6 +26,27 @@ export interface ConversationTransportMetadata { chatType?: string; } +/** Transport metadata for macOS desktop clients, including host environment fields. */ +export interface MacosTransportMetadata extends BaseTransportMetadata { + /** Interface identifier for macOS transport. */ + interfaceId: "macos"; + /** Home directory of the host macOS user. */ + hostHomeDir?: string; + /** Username of the host macOS user. */ + hostUsername?: string; +} + +/** Transport metadata for non-macOS transports. */ +export interface NonMacosTransportMetadata extends BaseTransportMetadata { + /** Interface identifier for this transport (e.g. "ios", "cli"). */ + interfaceId?: Exclude; +} + +/** Lightweight conversation transport metadata for channel identity and natural-language guidance. */ +export type ConversationTransportMetadata = + | MacosTransportMetadata + | NonMacosTransportMetadata; + export interface ConversationCreateRequest { type: "conversation_create"; title?: string; diff --git a/assistant/src/daemon/server.ts b/assistant/src/daemon/server.ts index 110668954e9..ef530e9ba48 100644 --- a/assistant/src/daemon/server.ts +++ b/assistant/src/daemon/server.ts @@ -1075,7 +1075,7 @@ export class DaemonServer { // Guard: don't replace an active proxy during concurrent turn races — // another request may have started processing between the isProcessing() // check above and the await on ensureActorScopedHistory(). - if (resolvedInterface === "macos" || resolvedInterface === "ios") { + if (resolvedInterface === "macos") { if (!conversation.isProcessing() || !conversation.hostBashProxy) { conversation.setHostBashProxy( new HostBashProxy(conversation.getCurrentSender(), (requestId) => { diff --git a/assistant/src/runtime/routes/conversation-routes.ts b/assistant/src/runtime/routes/conversation-routes.ts index 23ee47a142a..5e9e81a9c7d 100644 --- a/assistant/src/runtime/routes/conversation-routes.ts +++ b/assistant/src/runtime/routes/conversation-routes.ts @@ -643,7 +643,9 @@ function isToolResultType(type: string): boolean { function isSystemNoticeText(block: Record): boolean { if (block.type !== "text") return false; const text = typeof block.text === "string" ? block.text : ""; - return text.startsWith("") && text.endsWith(""); + return ( + text.startsWith("") && text.endsWith("") + ); } /** @@ -1115,7 +1117,7 @@ export async function handleSendMessage( // channels, headless) fall back to local execution. // Set the proxy BEFORE updateClient so updateClient's call to // hostBashProxy.updateSender targets the correct (new) proxy. - if (sourceInterface === "macos" || sourceInterface === "ios") { + if (sourceInterface === "macos") { // Reuse the existing proxy if the conversation is actively processing a // host bash request to avoid orphaning in-flight requests. if (!conversation.isProcessing() || !conversation.hostBashProxy) { @@ -1152,9 +1154,7 @@ export async function handleSendMessage( // When proxies are preserved during an active turn (non-desktop request while // processing), skip updating proxy senders to avoid degrading them. const preservingProxies = - conversation.isProcessing() && - sourceInterface !== "macos" && - sourceInterface !== "ios"; + conversation.isProcessing() && sourceInterface !== "macos"; conversation.updateClient(onEvent, !isInteractive, { skipProxySenderUpdate: preservingProxies, }); diff --git a/clients/shared/Network/Generated/GeneratedAPITypes.swift b/clients/shared/Network/Generated/GeneratedAPITypes.swift index 06e5711b6b4..7797e76a57a 100644 --- a/clients/shared/Network/Generated/GeneratedAPITypes.swift +++ b/clients/shared/Network/Generated/GeneratedAPITypes.swift @@ -3588,12 +3588,18 @@ public struct ConversationTransportMetadata: Codable, Sendable { public let hints: [String]? /// Optional concise UX brief for this channel. public let uxBrief: String? + /// Home directory of the host macOS user. Only populated when interfaceId == "macos". + public let hostHomeDir: String? + /// Username of the host macOS user. Only populated when interfaceId == "macos". + public let hostUsername: String? - public init(channelId: String, interfaceId: String? = nil, hints: [String]? = nil, uxBrief: String? = nil) { + public init(channelId: String, interfaceId: String? = nil, hints: [String]? = nil, uxBrief: String? = nil, hostHomeDir: String? = nil, hostUsername: String? = nil) { self.channelId = channelId self.interfaceId = interfaceId self.hints = hints self.uxBrief = uxBrief + self.hostHomeDir = hostHomeDir + self.hostUsername = hostUsername } }