From 11295cdbf4872ff1d22425a5afc4eb130fc38f11 Mon Sep 17 00:00:00 2001 From: Delicious233 <101502465+DeliciousBuding@users.noreply.github.com> Date: Tue, 21 Jul 2026 01:55:22 +0800 Subject: [PATCH] refactor(mobile): thin hubClient Proxy over shared SSOT Closes #1337 Closes #1338 Closes #1339 --- AGENTS.md | 2 + app/mobile-rn/README.md | 2 + app/mobile-rn/src/api/hubClient.ts | 525 +++++------------------------ 3 files changed, 93 insertions(+), 436 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index dbacce115..810724ce3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,8 +56,10 @@ Mobile 主线是 Expo + React Native development build。旧 Tauri Mobile 不再 - API 契约写在 `api/`。 - 通用 UI、transcript、composer、inspector、platform contract 放 `app/shared/`。 +- Hub REST/WS 方法与 DTO 的 SSOT 是 `app/shared/src/hubClient.ts`(及拆出的 payload/extended 模块)。Desktop/Web/Mobile 的 `src/api/hubClient.ts` 只能是 thin shell(平台默认 baseUrl、Tauri proxy、SecureStore token、fixture snapshot、WS URL 等胶水);禁止在客户端再分叉 REST 实现。 - Desktop 只能把 Tauri/Rust native 能力放在 `app/desktop/src-tauri/`。 - Web 只能通过 Hub/Web adapter 访问远端能力,不能直连 Local Edge 或 runtime。 +- Mobile(Expo/RN)同样 **Hub-only**:只走 Hub 合同与 shared hubClient;不得直连 Local Edge、raw runtime 或 Desktop host。详见 `app/mobile-rn/README.md`。 - Desktop renderer 不获得 raw process execution 权限;本地执行由 Local Edge 和 Tauri host typed API 承担。 ## 3. 产品术语 diff --git a/app/mobile-rn/README.md b/app/mobile-rn/README.md index d3a13154e..adf34b6f2 100644 --- a/app/mobile-rn/README.md +++ b/app/mobile-rn/README.md @@ -8,6 +8,8 @@ Historical longform notes are indexed in [docs/history.md](../../docs/history.md - Keep Mobile aligned with Desktop/Web workbench terminology and Hub event contracts. - Keep shared imports RN-safe; do not import shared Web/Desktop UI, CSS modules, Tauri APIs, browser storage, or raw runtime execution code. +- **Hub-only data plane**: Mobile talks to Hub (`/client/*`, `/web/*` via shared hubClient). It must **not** open Local Edge (`127.0.0.1:3210`), raw process/runtime APIs, or Desktop Tauri host commands. Local execution remains Desktop + Local Edge. +- Hub client SSOT is `@agenthub/shared/hubClient`. `src/api/hubClient.ts` is a thin shell (async SecureStore token, fixture snapshot, WS helpers only). Do not add new REST methods on Mobile — add them to shared first. - Treat Feishu/Lark mobile screenshots as density and interaction references only. - Do not claim native/device/live-Hub/TokenDance-ID proof unless the relevant approved-real or development-build gate was run in the current task and evidence was recorded. diff --git a/app/mobile-rn/src/api/hubClient.ts b/app/mobile-rn/src/api/hubClient.ts index 33ae6fb22..7da1d2cd2 100644 --- a/app/mobile-rn/src/api/hubClient.ts +++ b/app/mobile-rn/src/api/hubClient.ts @@ -1,134 +1,34 @@ +// Mobile Hub client — thin surface over shared SSOT (#1338 / T80.2). +// - Method/DTO SSOT: @agenthub/shared/hubClient +// - Mobile-only glue: async SecureStore token cache, fixture snapshot, legacy WS types, +// HubApiError/HubNetworkError (test/UI compatibility) +// Do NOT add new Hub REST methods here; add them to app/shared/src/hubClient.ts first. +// +// Inventory (T80.1 keep vs re-export): +// | Surface | Decision | +// |---|---| +// | REST methods (auth, sessions, messages, tasks, reactions, attachments, …) | re-export / Proxy → shared | +// | DTOs (HubAgentRunEvent, HubAgentTaskApproval, HubProbeAttachmentResponse, …) | re-export from shared | +// | createHubClient token cache + onRefreshToken | KEEP (async SecureStore) | +// | getMobileSnapshot + mapSessionsToMobileFixture | KEEP (mobile fixture shape) | +// | createMockHubClient | KEEP (fixture delay + mock snapshot) | +// | HubApiError / HubNetworkError | KEEP (test + historical UI imports) | +// | WS_BEARER_SUBPROTOCOL, buildWSAuthProtocols, createHubWsUrl, HubWs* | KEEP (RN WS layer) | +// | Legacy HubWsEventType mobile-only names | KEEP (App/UI compatibility) | + import { createHubClient as createSharedHubClient, - type HubClientOptions, type HubClient as SharedHubClient, - type HubOidcAuthorizeRequest, - type HubOidcAuthorizeResponse, - type HubOidcCallbackRequest, - type HubOidcCallbackResponse, - type HubUserProfile, - type HubAuthResponse, - type HubMessage, - type HubSearchResult, - type HubFriendRequest, + type HubClientOptions, type HubContactInfo, - type HubCustomAgent, - type HubCustomAgentRequest, - type HubExecutionTarget, - type HubListResponse, - type HubRegisterRequest, - type HubLoginRequest, - type HubUpdateProfileRequest, - type HubChangePasswordRequest, - type HubRegisterDeviceRequest, - type HubDevice, - type HubAddAgentToSessionRequest, - type HubAgentTask, - type HubTriggerAgentTaskOptions, - type HubNotification, - type HubCreatePrivateSessionRequest, - type HubCreateGroupSessionRequest, - type HubUpdateSessionInfoRequest, - type HubUpdateSessionSettingsRequest, type HubSession, - type HubSkill, - type HubMCPServer, } from '@agenthub/shared/hubClient'; import { mobileFixture } from '@/data/mobileFixtures'; import type { MobileAppFixture } from '@/types'; -// ── Local types for Hub methods not yet in shared hubClient ── - -export interface HubAgentRunEvent { - id: string; - task_id: string; - edge_run_id?: string; - session_id: string; - agent_instance_id: string; - event_seq: number; - event_type: string; - payload: unknown; - created_at: string; -} - -export interface HubAgentRunEventSummary { - task_id: string; - edge_run_id?: string; - status: string; - total_events: number; - last_event_seq: number; - event_type_counts: Record; - tool_call_count: number; - step_count: number; - artifact_count: number; - approval_count: number; - pending_approvals: number; - decided_approvals: number; - input_tokens: number; - output_tokens: number; - output_bytes: number; - started_at?: string; - finished_at?: string; - elapsed_ms?: number; -} - -export interface HubAgentTaskApproval { - approval_id: string; - task_id?: string; - edge_run_id?: string; - session_id?: string; - source_event_id?: string; - event_seq?: number; - request_id?: string; - tool_name?: string; - tool_use_id?: string; - status?: string; - reason?: string; - decided_by?: string; - created_at?: string; - decided_at?: string; - edge_control?: Record; -} - -export interface HubAgentTaskApprovalList { - task_id: string; - edge_run_id?: string; - session_id?: string; - approvals: HubAgentTaskApproval[]; - pending?: HubAgentTaskApproval[]; - decided?: HubAgentTaskApproval[]; - last_event_seq?: number; -} - -export interface HubAgentTaskArtifactList { - task_id: string; - edge_run_id?: string; - session_id?: string; - artifacts: Record[]; - last_event_seq?: number; -} - -export interface HubTaskApprovalDecisionRequest { - decision: 'allow' | 'deny'; - reason?: string; -} - -export interface HubAttachmentRef { - id: string; - hash: string; - size: number; - mime_type: string; - original_name?: string; - uploader_user_id?: string; - metadata?: string; - created_at?: string; -} - -export interface HubProbeAttachmentResponse { - exists: boolean; - attachment?: HubAttachmentRef; -} +// Re-export full shared SSOT for app imports (types + helpers). +export * from '@agenthub/shared/hubClient'; // ── Mobile-specific error types (preserved for test compatibility) ── @@ -259,182 +159,78 @@ export interface CreateHubClientOptions { fetchImpl?: typeof globalThis.fetch; } -export interface HubClient { - // Re-export the shared Hub client for direct access +/** Mobile Hub client = shared SSOT + mobile glue (snapshot / shared handle). */ +export type HubClient = SharedHubClient & { readonly shared: SharedHubClient; - // Legacy mobile snapshot (for fixture/fallback mode) getMobileSnapshot: () => Promise; - // Auth - oidcAuthorize: (body: HubOidcAuthorizeRequest) => Promise; - oidcCallback: (body: HubOidcCallbackRequest) => Promise; - register: (body: HubRegisterRequest) => Promise<{ user_id: string }>; - login: (body: HubLoginRequest) => Promise; - refresh: (refreshToken: string) => Promise; - logout: () => Promise; - me: () => Promise; - updateProfile: (body: HubUpdateProfileRequest) => Promise; - changePassword: (body: HubChangePasswordRequest) => Promise; - // Sessions - listSessions: () => Promise; - searchSessions: (q: string) => Promise; - createPrivateSession: (body: HubCreatePrivateSessionRequest) => Promise; - createGroupSession: (body: HubCreateGroupSessionRequest) => Promise; - addSessionMembers: (sessionId: string, memberIds: string[]) => Promise; - removeSessionMember: (sessionId: string, userId: string) => Promise; - leaveSession: (sessionId: string) => Promise; - dissolveSession: (sessionId: string) => Promise; - updateSessionInfo: (sessionId: string, body: HubUpdateSessionInfoRequest) => Promise; - updateSessionSettings: (sessionId: string, body: HubUpdateSessionSettingsRequest) => Promise; - deleteSession: (sessionId: string) => Promise; - // Messages - sendMessage: (sessionId: string, body: { client_msg_id: string; content_type: string; content: string }) => Promise<{ message_id: string; seq_id: number; created_at: string }>; - getMessages: (sessionId: string, params?: { before_seq?: number; limit?: number }) => Promise; - syncMessages: (sessionId: string, params?: { after_seq?: number; limit?: number }) => Promise; - markRead: (sessionId: string, lastReadSeq: number) => Promise; - recallMessage: (messageId: string) => Promise; - editMessage: (messageId: string, body: { content: string }) => Promise; - pinMessage: (messageId: string, sessionId: string) => Promise; - unpinMessage: (messageId: string, sessionId: string) => Promise; - forwardMessage: (messageId: string, targetSessionIds: string[]) => Promise; - listPinnedMessages: (sessionId: string) => Promise; - searchMessages: (params: { q: string; session_id?: string; content_type?: string; from?: string; to?: string }) => Promise; - searchSessionMessages: (sessionId: string, params: { q: string; content_type?: string; from?: string; to?: string }) => Promise; - // Message reactions - addMessageReaction: (messageId: string, sessionId: string, reaction: { emoji: string }) => Promise; - removeMessageReaction: (messageId: string, sessionId: string, reaction: { emoji: string }) => Promise; - listMessageReactions: (messageId: string, sessionId: string) => Promise[]>; - // Contacts - searchUser: (targetUserId: string) => Promise; - listContacts: () => Promise; - sendFriendRequest: (friendId: string, message?: string) => Promise; - listFriendRequests: () => Promise; - acceptFriendRequest: (requestId: string) => Promise; - rejectFriendRequest: (requestId: string) => Promise; - blockContact: (targetUserId: string) => Promise; - unblockContact: (targetUserId: string) => Promise; - updateContactRemark: (friendUserId: string, remark: string) => Promise; - removeContact: (friendUserId: string) => Promise; - // Notifications - listNotifications: (params?: { unread_only?: boolean; limit?: number; offset?: number }) => Promise; - markNotificationRead: (id: string) => Promise; - readAllNotifications: () => Promise; - // Devices - registerDevice: (body: HubRegisterDeviceRequest) => Promise; - // Custom Agents - listCustomAgents: () => Promise; - createCustomAgent: (body: HubCustomAgentRequest) => Promise; - updateCustomAgent: (id: string, body: HubCustomAgentRequest) => Promise; - deleteCustomAgent: (id: string) => Promise; - // Agent tasks - addAgentToSession: (sessionId: string, body: HubAddAgentToSessionRequest) => Promise; - triggerAgentTask: (triggerMessageId: string, options?: HubTriggerAgentTaskOptions) => Promise; - cancelAgentTask: (taskId: string) => Promise; - regenerateAgentTask: (taskId: string) => Promise; - listTaskRunEvents: (taskId: string) => Promise; - listTaskRunEventsAfter: (taskId: string, afterSeq: number) => Promise; - getTaskRunEventSummary: (taskId: string) => Promise; - listTaskApprovals: (taskId: string) => Promise; - decideTaskApproval: (taskId: string, approvalId: string, decision: HubTaskApprovalDecisionRequest) => Promise; - listTaskArtifacts: (taskId: string) => Promise; - // Execution Targets - listExecutionTargets: () => Promise>; - // Skills & MCP - listPublicSkills: (params?: { skill_type?: string; q?: string; is_public?: string; pageCursor?: string; pageSize?: number }) => Promise>; - listPublicMCPServers: (params?: { transport?: string; q?: string; is_public?: string; pageCursor?: string; pageSize?: number }) => Promise>; - // Edge task lifecycle - ackTask: (taskId: string, runId?: string) => Promise; - streamTask: (taskId: string, content: string, runId?: string) => Promise; - doneTask: (taskId: string, finalContent?: string, runId?: string) => Promise; - failTask: (taskId: string, error: string, runId?: string) => Promise; - // Attachments - probeAttachment: (hash: string) => Promise; - downloadAttachmentUrl: (attachmentId: string) => string; +}; + +/** Methods that must not wait on SecureStore token resolution. */ +const NO_AUTH_METHOD_KEYS = new Set([ + 'oidcAuthorize', + 'oidcCallback', + 'register', + 'login', + 'refresh', + // Sync URL builder — no network; shared already prefixes baseUrl. + 'downloadAttachmentUrl', +]); + +function wrapSharedAsMobileClient( + shared: SharedHubClient, + glue: { + ensureToken: () => Promise; + withAuth: (fn: () => Promise) => Promise; + clearToken: () => void; + getMobileSnapshot: () => Promise; + }, +): HubClient { + return new Proxy(shared as object, { + get(target, prop, receiver) { + if (prop === 'shared') return shared; + if (prop === 'getMobileSnapshot') return glue.getMobileSnapshot; + + if (prop === 'logout') { + return async () => { + try { + await glue.ensureToken(); + await shared.logout(); + } finally { + glue.clearToken(); + } + }; + } + + const value = Reflect.get(target, prop, receiver); + if (typeof value !== 'function') { + return value; + } + + const key = String(prop); + if (NO_AUTH_METHOD_KEYS.has(key)) { + return (value as (...args: unknown[]) => unknown).bind(target); + } + + return (...args: unknown[]) => + glue.withAuth(() => (value as (...a: unknown[]) => Promise).apply(target, args)); + }, + }) as HubClient; } export function createMockHubClient(delayMs = 80): HubClient { const shared = createSharedHubClient(); - return { - shared, - async getMobileSnapshot() { + return wrapSharedAsMobileClient(shared, { + ensureToken: async () => null, + withAuth: async (fn) => fn(), + clearToken: () => {}, + getMobileSnapshot: async () => { await new Promise((resolve) => { setTimeout(resolve, delayMs); }); return mobileFixture; }, - oidcAuthorize: () => { throw new Error('Mock: OIDC not available'); }, - oidcCallback: () => { throw new Error('Mock: OIDC not available'); }, - register: () => { throw new Error('Mock: register not available'); }, - login: () => { throw new Error('Mock: login not available'); }, - refresh: () => { throw new Error('Mock: refresh not available'); }, - logout: async () => {}, - me: () => { throw new Error('Mock: me not available'); }, - updateProfile: () => { throw new Error('Mock: updateProfile not available'); }, - changePassword: async () => {}, - listSessions: async () => [], - searchSessions: async () => [], - createPrivateSession: () => { throw new Error('Mock: createPrivateSession not available'); }, - createGroupSession: () => { throw new Error('Mock: createGroupSession not available'); }, - addSessionMembers: async () => {}, - removeSessionMember: async () => {}, - leaveSession: async () => {}, - dissolveSession: async () => {}, - updateSessionInfo: async () => {}, - updateSessionSettings: async () => {}, - deleteSession: async () => {}, - sendMessage: () => { throw new Error('Mock: sendMessage not available'); }, - getMessages: async () => [], - syncMessages: async () => [], - markRead: async () => {}, - recallMessage: async () => {}, - editMessage: () => { throw new Error('Mock: editMessage not available'); }, - pinMessage: async () => {}, - unpinMessage: async () => {}, - forwardMessage: async () => {}, - listPinnedMessages: async () => [], - searchMessages: async () => [], - searchSessionMessages: async () => [], - addMessageReaction: async () => {}, - removeMessageReaction: async () => {}, - listMessageReactions: async () => [], - searchUser: () => { throw new Error('Mock: searchUser not available'); }, - listContacts: async () => [], - sendFriendRequest: async () => {}, - listFriendRequests: async () => [], - acceptFriendRequest: async () => {}, - rejectFriendRequest: async () => {}, - blockContact: async () => {}, - unblockContact: async () => {}, - updateContactRemark: async () => {}, - removeContact: async () => {}, - listNotifications: async () => [], - markNotificationRead: async () => {}, - readAllNotifications: async () => {}, - registerDevice: () => { throw new Error('Mock: registerDevice not available'); }, - listCustomAgents: async () => [], - createCustomAgent: () => { throw new Error('Mock: createCustomAgent not available'); }, - updateCustomAgent: async () => {}, - deleteCustomAgent: async () => {}, - addAgentToSession: async () => {}, - triggerAgentTask: () => { throw new Error('Mock: triggerAgentTask not available'); }, - cancelAgentTask: async () => {}, - regenerateAgentTask: () => { throw new Error('Mock: regenerateAgentTask not available'); }, - listTaskRunEvents: async () => [], - listTaskRunEventsAfter: async () => [], - getTaskRunEventSummary: () => { throw new Error('Mock: getTaskRunEventSummary not available'); }, - listTaskApprovals: async () => ({ approvals: [], task_id: '' }), - decideTaskApproval: () => { throw new Error('Mock: decideTaskApproval not available'); }, - listTaskArtifacts: async () => ({ artifacts: [], task_id: '' }), - listExecutionTargets: async () => ({ items: [], page: { hasMore: false } }), - listPublicSkills: async () => ({ items: [], page: { hasMore: false } }), - listPublicMCPServers: async () => ({ items: [], page: { hasMore: false } }), - ackTask: async () => {}, - streamTask: async () => {}, - doneTask: async () => {}, - failTask: async () => {}, - probeAttachment: async () => ({ exists: false }), - downloadAttachmentUrl: (attachmentId: string) => `/mock/attachments/${attachmentId}`, - }; + }); } export function createHubClient(options: CreateHubClientOptions): HubClient { @@ -509,23 +305,15 @@ export function createHubClient(options: CreateHubClientOptions): HubClient { const shared = createSharedHubClient(sharedOpts); - // Helper for building query strings - function qs(params: Record): string { - const p = new URLSearchParams(); - for (const [k, v] of Object.entries(params)) { - if (v != null) p.set(k, String(v)); - } - const s = p.toString(); - return s ? `?${s}` : ''; - } - - return { - shared, - - async getMobileSnapshot() { + return wrapSharedAsMobileClient(shared, { + ensureToken, + withAuth, + clearToken: () => { + cachedToken = null; + }, + getMobileSnapshot: async () => { await ensureToken(); - // Build a mobile snapshot from real Hub API data const [sessions, contacts] = await Promise.all([ shared.listSessions().catch(() => [] as HubSession[]), shared.listContacts().catch(() => [] as HubContactInfo[]), @@ -533,142 +321,7 @@ export function createHubClient(options: CreateHubClientOptions): HubClient { return mapSessionsToMobileFixture(sessions, contacts); }, - - // Unauthenticated entry points — no token gate. - oidcAuthorize: (body) => shared.oidcAuthorize(body), - oidcCallback: (body) => shared.oidcCallback(body), - register: (body) => shared.register(body), - login: (body) => shared.login(body), - refresh: (refreshToken) => shared.refresh(refreshToken), - logout: async () => { - try { - await ensureToken(); - await shared.logout(); - } finally { - cachedToken = null; - } - }, - me: () => withAuth(() => shared.me()), - updateProfile: (body) => withAuth(() => shared.updateProfile(body)), - changePassword: (body) => withAuth(() => shared.changePassword(body)), - listSessions: () => withAuth(() => shared.listSessions()), - searchSessions: (q) => withAuth(() => shared.searchSessions(q)), - createPrivateSession: (body) => withAuth(() => shared.createPrivateSession(body)), - createGroupSession: (body) => withAuth(() => shared.createGroupSession(body)), - addSessionMembers: (sessionId, memberIds) => withAuth(() => shared.addSessionMembers(sessionId, memberIds)), - removeSessionMember: (sessionId, userId) => withAuth(() => shared.removeSessionMember(sessionId, userId)), - leaveSession: (sessionId) => withAuth(() => shared.leaveSession(sessionId)), - dissolveSession: (sessionId) => withAuth(() => shared.dissolveSession(sessionId)), - updateSessionInfo: (sessionId, body) => withAuth(() => shared.updateSessionInfo(sessionId, body)), - updateSessionSettings: (sessionId, body) => withAuth(() => shared.updateSessionSettings(sessionId, body)), - deleteSession: (sessionId) => withAuth(() => shared.deleteSession(sessionId)), - sendMessage: (sessionId, body) => withAuth(() => shared.sendMessage(sessionId, body)), - getMessages: (sessionId, params) => withAuth(() => shared.getMessages(sessionId, params)), - syncMessages: (sessionId, params) => withAuth(() => shared.syncMessages(sessionId, params)), - markRead: (sessionId, lastReadSeq) => withAuth(() => shared.markRead(sessionId, lastReadSeq)), - recallMessage: (messageId) => withAuth(() => shared.recallMessage(messageId)), - editMessage: (messageId, body) => - withAuth(() => - shared.request(`/client/messages/${encodeURIComponent(messageId)}`, { - method: 'PUT', - body: JSON.stringify(body), - }), - ), - pinMessage: (messageId, sessionId) => withAuth(() => shared.pinMessage(messageId, sessionId)), - unpinMessage: (messageId, sessionId) => withAuth(() => shared.unpinMessage(messageId, sessionId)), - forwardMessage: (messageId, targetSessionIds) => withAuth(() => shared.forwardMessage(messageId, targetSessionIds)), - listPinnedMessages: (sessionId) => withAuth(() => shared.listPinnedMessages(sessionId)), - searchMessages: (params) => withAuth(() => shared.searchMessages(params)), - searchSessionMessages: (sessionId, params) => withAuth(() => shared.searchSessionMessages(sessionId, params)), - addMessageReaction: (messageId, sessionId, reaction) => - withAuth(() => - // eslint-disable-next-line @typescript-eslint/no-invalid-void-type - shared.request(`/client/messages/${encodeURIComponent(messageId)}/reactions`, { - method: 'POST', - body: JSON.stringify({ session_id: sessionId, ...reaction }), - }), - ), - removeMessageReaction: (messageId, sessionId, reaction) => - withAuth(() => - // eslint-disable-next-line @typescript-eslint/no-invalid-void-type - shared.request(`/client/messages/${encodeURIComponent(messageId)}/reactions`, { - method: 'DELETE', - body: JSON.stringify({ session_id: sessionId, ...reaction }), - }), - ), - listMessageReactions: (messageId, sessionId) => - withAuth(() => - shared.request[]>(`/client/messages/${encodeURIComponent(messageId)}/reactions?session_id=${encodeURIComponent(sessionId)}`), - ), - searchUser: (targetUserId) => withAuth(() => shared.searchUser(targetUserId)), - listContacts: () => withAuth(() => shared.listContacts()), - sendFriendRequest: (friendId, message) => withAuth(() => shared.sendFriendRequest(friendId, message)), - listFriendRequests: () => withAuth(() => shared.listFriendRequests()), - acceptFriendRequest: (requestId) => withAuth(() => shared.acceptFriendRequest(requestId)), - rejectFriendRequest: (requestId) => withAuth(() => shared.rejectFriendRequest(requestId)), - blockContact: (targetUserId) => withAuth(() => shared.blockContact(targetUserId)), - unblockContact: (targetUserId) => withAuth(() => shared.unblockContact(targetUserId)), - updateContactRemark: (friendUserId, remark) => withAuth(() => shared.updateContactRemark(friendUserId, remark)), - removeContact: (friendUserId) => withAuth(() => shared.removeContact(friendUserId)), - listNotifications: (params) => withAuth(() => shared.listNotifications(params)), - markNotificationRead: (id) => withAuth(() => shared.markNotificationRead(id)), - readAllNotifications: () => withAuth(() => shared.readAllNotifications()), - registerDevice: (body) => withAuth(() => shared.registerDevice(body)), - listCustomAgents: () => withAuth(() => shared.listCustomAgents()), - createCustomAgent: (body) => withAuth(() => shared.createCustomAgent(body)), - updateCustomAgent: (id, body) => withAuth(() => shared.updateCustomAgent(id, body)), - deleteCustomAgent: (id) => withAuth(() => shared.deleteCustomAgent(id)), - addAgentToSession: (sessionId, body) => - withAuth(async () => { - await shared.addAgentToSession(sessionId, body); - }), - triggerAgentTask: (triggerMessageId, taskOptions) => withAuth(() => shared.triggerAgentTask(triggerMessageId, taskOptions)), - cancelAgentTask: (taskId) => withAuth(() => shared.cancelAgentTask(taskId)), - regenerateAgentTask: (taskId) => withAuth(() => shared.regenerateAgentTask(taskId)), - listTaskRunEvents: (taskId) => - withAuth(() => - shared.request(`/web/agent-tasks/${encodeURIComponent(taskId)}/events`), - ), - listTaskRunEventsAfter: (taskId, afterSeq) => - withAuth(() => - shared.request(`/web/agent-tasks/${encodeURIComponent(taskId)}/events${qs({ after_seq: afterSeq, limit: 500 })}`), - ), - getTaskRunEventSummary: (taskId) => - withAuth(() => - shared.request(`/web/agent-tasks/${encodeURIComponent(taskId)}/summary`), - ), - listTaskApprovals: (taskId) => - withAuth(() => - shared.request(`/web/agent-tasks/${encodeURIComponent(taskId)}/approvals`), - ), - decideTaskApproval: (taskId, approvalId, decision) => - withAuth(() => - shared.request(`/web/agent-tasks/${encodeURIComponent(taskId)}/approvals/${encodeURIComponent(approvalId)}/decide`, { - method: 'POST', - body: JSON.stringify(decision), - }), - ), - listTaskArtifacts: (taskId) => - withAuth(() => - shared.request(`/web/agent-tasks/${encodeURIComponent(taskId)}/artifacts`), - ), - listExecutionTargets: () => withAuth(() => shared.listExecutionTargets()), - listPublicSkills: (params) => withAuth(() => shared.listPublicSkills(params)), - listPublicMCPServers: (params) => withAuth(() => shared.listPublicMCPServers(params)), - ackTask: (taskId, runId) => withAuth(() => shared.ackTask(taskId, runId)), - streamTask: (taskId, content, runId) => withAuth(() => shared.streamTask(taskId, content, runId)), - doneTask: (taskId, finalContent, runId) => withAuth(() => shared.doneTask(taskId, finalContent, runId)), - failTask: (taskId, error, runId) => withAuth(() => shared.failTask(taskId, error, runId)), - probeAttachment: (hash) => - withAuth(() => - shared.request('/client/attachments/probe', { - method: 'POST', - body: JSON.stringify({ hash }), - }), - ), - downloadAttachmentUrl: (attachmentId) => - `${options.baseUrl.replace(/\/+$/, '')}/client/attachments/${encodeURIComponent(attachmentId)}`, - }; + }); } // ── WebSocket URL builder (aligned with hub-server /client/ws) ──