@puredesktop/puredesktop-ui-bridge 2.1.8 → 2.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/package.json +35 -5
  2. package/src/agents/mcpServerPresets.test.ts +105 -0
  3. package/src/agents/mcpServerPresets.ts +380 -0
  4. package/src/agents/runtime/index.ts +7 -0
  5. package/src/agents/runtime/mapAgentSnapshot.ts +11 -8
  6. package/src/agents/runtime/pendingAsks.test.ts +117 -0
  7. package/src/agents/runtime/pendingAsks.ts +110 -0
  8. package/src/bridge/agentScopeDefaults.ts +65 -0
  9. package/src/bridge/agentTypes.ts +36 -2
  10. package/src/bridge/agents.d.mts +6 -0
  11. package/src/bridge/agents.mjs +10 -0
  12. package/src/bridge/agents.ts +4 -0
  13. package/src/bridge/credentials.d.mts +62 -0
  14. package/src/bridge/credentials.mjs +53 -0
  15. package/src/bridge/events.d.mts +1 -0
  16. package/src/bridge/events.mjs +1 -0
  17. package/src/bridge/googleAuth.ts +7 -152
  18. package/src/bridge/mcpClient.d.mts +74 -0
  19. package/src/bridge/mcpClient.mjs +79 -0
  20. package/src/bridge/methods.d.mts +22 -0
  21. package/src/bridge/methods.mjs +23 -0
  22. package/src/bridge/notifications.d.mts +12 -0
  23. package/src/bridge/notifications.mjs +36 -0
  24. package/src/bridge/operations.d.mts +44 -0
  25. package/src/bridge/operations.mjs +40 -0
  26. package/src/bridge/react/usePlatformAgentSessionDrive.test.tsx +29 -10
  27. package/src/bridge/react/usePlatformAgentSessionDrive.tsx +10 -81
  28. package/src/bridge/react/usePlatformAppSettings.test.tsx +105 -105
  29. package/src/bridge/react/usePlatformAppSettings.tsx +104 -104
  30. package/src/bridge/react/usePlatformJsonStore.test.tsx +152 -152
  31. package/src/bridge/react/usePlatformJsonStore.tsx +149 -149
  32. package/src/bridge/react/usePlatformOperations.tsx +75 -0
  33. package/src/bridge/storage.d.mts +17 -4
  34. package/src/bridge/storage.mjs +6 -3
  35. package/src/components/agents/AgentComposer.tsx +10 -6
  36. package/src/components/agents/AgentDrawerPanel.test.tsx +2 -205
  37. package/src/components/agents/AgentDrawerPanel.tsx +23 -64
  38. package/src/components/agents/AgentMessageBubble.tsx +59 -33
  39. package/src/components/agents/AgentMessageList.tsx +4 -9
  40. package/src/components/agents/AgentQuestionPromptList.tsx +34 -212
  41. package/src/components/agents/AgentToolApprovalActions.tsx +13 -11
  42. package/src/components/agents/AgentToolPendingList.tsx +6 -6
  43. package/src/components/agents/AgentToolPendingReview.tsx +6 -6
  44. package/src/components/agents/QuestionRequestList.tsx +278 -0
  45. package/src/components/agents/agentPanelStyles.ts +9 -33
  46. package/src/components/agents/agentTypes.ts +25 -43
  47. package/src/components/agents/index.ts +4 -0
  48. package/src/components/assets/asset-library/sidebar/AssetLibraryList.tsx +10 -4
  49. package/src/components/chrome/WorkspaceTabStrip.tsx +4 -3
  50. package/src/components/common/chat/ChatBox.tsx +9 -9
  51. package/src/components/common/chat/ChatModelMenu.tsx +34 -4
  52. package/src/components/common/chat/ChatThread.tsx +23 -7
  53. package/src/components/common/connections/ProviderConnection.test.tsx +208 -0
  54. package/src/components/common/connections/ProviderConnection.tsx +277 -0
  55. package/src/components/common/connections/index.ts +5 -0
  56. package/src/components/common/containers/AppFrame.tsx +26 -19
  57. package/src/components/common/containers/AppHeader.tsx +20 -20
  58. package/src/components/common/documents/DocumentSwitcher.tsx +63 -58
  59. package/src/components/common/dropdown/MenuDropdownItem.tsx +14 -2
  60. package/src/components/common/dropdown/MenuPortal.tsx +3 -1
  61. package/src/components/common/dropdown/menuViewportPosition.test.ts +20 -10
  62. package/src/components/common/dropdown/menuViewportPosition.ts +35 -27
  63. package/src/components/common/overlays/Modal.tsx +13 -2
  64. package/src/components/editor/EditorLinkPromptDialog.tsx +1 -1
  65. package/src/ics/generateIcs.test.ts +153 -0
  66. package/src/ics/generateIcs.ts +197 -0
  67. package/src/index.ts +3 -0
  68. package/src/net/httpRetry.test.ts +117 -0
  69. package/src/net/httpRetry.ts +111 -0
  70. package/src/theme/appAccents.ts +6 -0
  71. package/src/theme/appIdentityCss.mjs +11 -0
  72. package/src/theme/appIdentityCss.ts +11 -0
  73. package/src/theme/themes/dark.ts +3 -1
  74. package/src/theme/themes/light.ts +3 -1
  75. package/src/bridge/googleProviderConfig.ts +0 -73
@@ -0,0 +1,117 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { PlatformAgentMessage } from '../../bridge/agentTypes.js'
3
+ import {
4
+ derivePendingAsks,
5
+ sessionPendingAskCalls,
6
+ type PendingAskSession,
7
+ } from './pendingAsks.js'
8
+
9
+ function pendingMessages(
10
+ calls: Array<{ id: string; name: string }>,
11
+ ): PlatformAgentMessage[] {
12
+ return [
13
+ { role: 'user', content: 'do the thing' },
14
+ {
15
+ role: 'assistant',
16
+ content: '',
17
+ toolCalls: calls.map(call => ({ ...call, arguments: {} })),
18
+ },
19
+ ]
20
+ }
21
+
22
+ function session(
23
+ overrides: Partial<PendingAskSession> = {},
24
+ ): PendingAskSession {
25
+ return {
26
+ scope: 'global',
27
+ sessionId: 'parent-1',
28
+ state: 'pending_tools',
29
+ messages: pendingMessages([{ id: 'call-1', name: 'harness.invoke_agent' }]),
30
+ ...overrides,
31
+ }
32
+ }
33
+
34
+ describe('sessionPendingAskCalls', () => {
35
+ it('lists unresolved pending calls', () => {
36
+ expect(sessionPendingAskCalls(session())).toEqual([
37
+ { id: 'call-1', name: 'harness.invoke_agent', arguments: {} },
38
+ ])
39
+ })
40
+
41
+ it('an accepted call is in flight, not an ask', () => {
42
+ expect(
43
+ sessionPendingAskCalls(
44
+ session({ acceptedPendingToolCallIds: ['call-1'] }),
45
+ ),
46
+ ).toEqual([])
47
+ })
48
+
49
+ it('a settled session has no asks', () => {
50
+ expect(
51
+ sessionPendingAskCalls(session({ state: 'awaiting_user' })),
52
+ ).toEqual([])
53
+ })
54
+
55
+ it('policy-approved calls are not asks when a policy is supplied', () => {
56
+ expect(
57
+ sessionPendingAskCalls(
58
+ session({
59
+ toolPolicy: {
60
+ allowedTargetApps: [],
61
+ routeReadinessMs: 1_000,
62
+ handlerResponseMs: 1_000,
63
+ approvalOverrides: { 'harness.invoke_agent': false },
64
+ },
65
+ }),
66
+ ),
67
+ ).toEqual([])
68
+ })
69
+ })
70
+
71
+ describe('derivePendingAsks', () => {
72
+ it('an active child delegation ask outranks the parent ask', () => {
73
+ const asks = derivePendingAsks(session(), [
74
+ {
75
+ status: 'running',
76
+ childAppSlug: 'writer',
77
+ child: session({
78
+ scope: 'app',
79
+ sessionId: 'child-1',
80
+ appSlug: 'writer',
81
+ messages: pendingMessages([
82
+ { id: 'call-2', name: 'writer.createDocument' },
83
+ ]),
84
+ }),
85
+ },
86
+ ])
87
+
88
+ expect(asks).toHaveLength(2)
89
+ expect(asks[0]).toEqual({
90
+ session: { scope: 'app', sessionId: 'child-1', appSlug: 'writer' },
91
+ origin: 'child',
92
+ childAppSlug: 'writer',
93
+ calls: [{ id: 'call-2', name: 'writer.createDocument', arguments: {} }],
94
+ })
95
+ expect(asks[1]).toEqual(
96
+ expect.objectContaining({ origin: 'parent' }),
97
+ )
98
+ })
99
+
100
+ it('settled delegations and unreadable children are ignored', () => {
101
+ const asks = derivePendingAsks(session(), [
102
+ {
103
+ status: 'completed',
104
+ childAppSlug: 'writer',
105
+ child: session({ sessionId: 'child-done' }),
106
+ },
107
+ { status: 'running', childAppSlug: 'book', child: null },
108
+ ])
109
+ expect(asks).toEqual([expect.objectContaining({ origin: 'parent' })])
110
+ })
111
+
112
+ it('no asks anywhere yields an empty list', () => {
113
+ expect(
114
+ derivePendingAsks(session({ state: 'awaiting_user' })),
115
+ ).toEqual([])
116
+ })
117
+ })
@@ -0,0 +1,110 @@
1
+ import type {
2
+ PlatformAgentMessage,
3
+ PlatformAgentSessionScope,
4
+ PlatformAgentSessionToolPolicy,
5
+ } from '../../bridge/agentTypes.js'
6
+ import type { AgentUiToolCall } from '../../components/agents/agentTypes.js'
7
+ import { derivePendingToolCalls } from './pendingToolCalls.js'
8
+ import { toolRequiresApproval } from './toolRequiresApproval.js'
9
+
10
+ /**
11
+ * The one pending-asks selector (docs/09-missions.md §Card states): both the
12
+ * agent drawer and the mission card derive "what is this run waiting on the
13
+ * human for?" from here — manual pending calls minus accepted, with active
14
+ * child-delegation asks taking precedence over the parent's. No surface
15
+ * keeps its own derivation that can disagree. Approving from either surface
16
+ * is the same operation against the same session.
17
+ */
18
+
19
+ export interface PendingAskSession {
20
+ scope: PlatformAgentSessionScope
21
+ sessionId: string
22
+ appSlug?: string
23
+ state: string
24
+ messages: readonly PlatformAgentMessage[]
25
+ acceptedPendingToolCallIds?: readonly string[]
26
+ /** When supplied, calls the policy auto-approves are not asks. */
27
+ toolPolicy?: PlatformAgentSessionToolPolicy
28
+ }
29
+
30
+ export interface PendingAskDelegation {
31
+ status: string
32
+ childAppSlug: string
33
+ /** The delegated child's session, when the caller could read it. */
34
+ child?: PendingAskSession | null
35
+ }
36
+
37
+ export interface PendingAsk {
38
+ session: {
39
+ scope: PlatformAgentSessionScope
40
+ sessionId: string
41
+ appSlug?: string
42
+ }
43
+ /** Child asks come from an active delegated app agent. */
44
+ origin: 'child' | 'parent'
45
+ childAppSlug?: string
46
+ calls: AgentUiToolCall[]
47
+ }
48
+
49
+ /**
50
+ * One session's slice of the contract: unresolved manual calls, minus the
51
+ * accepted-but-still-settling ones (an accepted call is IN FLIGHT — a
52
+ * delegation or app tool holds it open — not waiting on the human).
53
+ */
54
+ export function sessionPendingAskCalls(
55
+ session: Pick<
56
+ PendingAskSession,
57
+ 'state' | 'messages' | 'acceptedPendingToolCallIds' | 'toolPolicy'
58
+ >,
59
+ ): AgentUiToolCall[] {
60
+ const accepted = new Set(session.acceptedPendingToolCallIds ?? [])
61
+ return derivePendingToolCalls(session.messages, session.state)
62
+ .filter(call => !accepted.has(call.id))
63
+ .filter(call =>
64
+ session.toolPolicy
65
+ ? toolRequiresApproval(call.name, session.toolPolicy)
66
+ : true,
67
+ )
68
+ .map(call => ({ id: call.id, name: call.name, arguments: call.arguments }))
69
+ }
70
+
71
+ function sessionRef(session: PendingAskSession): PendingAsk['session'] {
72
+ return {
73
+ scope: session.scope,
74
+ sessionId: session.sessionId,
75
+ ...(session.appSlug ? { appSlug: session.appSlug } : {}),
76
+ }
77
+ }
78
+
79
+ function delegationIsActive(status: string): boolean {
80
+ return status === 'pending' || status === 'running'
81
+ }
82
+
83
+ /**
84
+ * All open asks for one run, child-first: while a delegated app agent is
85
+ * asking, that ask IS the run's current question — a parent-level
86
+ * `harness.invoke_agent` ask shown instead would be stale.
87
+ */
88
+ export function derivePendingAsks(
89
+ parent: PendingAskSession,
90
+ delegations: readonly PendingAskDelegation[] = [],
91
+ ): PendingAsk[] {
92
+ const asks: PendingAsk[] = []
93
+ for (const delegation of delegations) {
94
+ if (!delegationIsActive(delegation.status)) continue
95
+ if (!delegation.child) continue
96
+ const calls = sessionPendingAskCalls(delegation.child)
97
+ if (calls.length === 0) continue
98
+ asks.push({
99
+ session: sessionRef(delegation.child),
100
+ origin: 'child',
101
+ childAppSlug: delegation.childAppSlug,
102
+ calls,
103
+ })
104
+ }
105
+ const parentCalls = sessionPendingAskCalls(parent)
106
+ if (parentCalls.length > 0) {
107
+ asks.push({ session: sessionRef(parent), origin: 'parent', calls: parentCalls })
108
+ }
109
+ return asks
110
+ }
@@ -0,0 +1,65 @@
1
+ import type {
2
+ PlatformAgentSessionConfigPatch,
3
+ PlatformAgentSessionScope,
4
+ } from './agentTypes.js'
5
+ import {
6
+ getPlatformPreferences,
7
+ patchPlatformPreferences,
8
+ } from './preferences.mjs'
9
+
10
+ /**
11
+ * Persist session-config changes as scope defaults in shell preferences
12
+ * (`agentSessionDefaults.global` / `.apps[slug]`) — layer 3 of the approval
13
+ * ladder (docs/08-agent-orchestration.md). Every NEW session in the scope
14
+ * inherits these, mission runs and delegated children included; this is what
15
+ * makes "always allow" stick beyond one conversation.
16
+ */
17
+
18
+ interface StoredScopeDefaults {
19
+ global?: PlatformAgentSessionConfigPatch
20
+ apps?: Record<string, PlatformAgentSessionConfigPatch>
21
+ }
22
+
23
+ export function mergeScopeDefaultPatch(
24
+ base: PlatformAgentSessionConfigPatch | undefined,
25
+ patch: PlatformAgentSessionConfigPatch,
26
+ ): PlatformAgentSessionConfigPatch {
27
+ const toolPolicy =
28
+ patch.toolPolicy || base?.toolPolicy
29
+ ? {
30
+ ...base?.toolPolicy,
31
+ ...patch.toolPolicy,
32
+ approvalOverrides: {
33
+ ...base?.toolPolicy?.approvalOverrides,
34
+ ...patch.toolPolicy?.approvalOverrides,
35
+ },
36
+ }
37
+ : undefined
38
+ return { ...base, ...patch, ...(toolPolicy ? { toolPolicy } : {}) }
39
+ }
40
+
41
+ export async function patchAgentSessionScopeDefaults(
42
+ scope: PlatformAgentSessionScope,
43
+ appSlug: string | undefined,
44
+ patch: PlatformAgentSessionConfigPatch,
45
+ ): Promise<void> {
46
+ const prefs = (await getPlatformPreferences()) as {
47
+ agentSessionDefaults?: StoredScopeDefaults
48
+ }
49
+ const stored = prefs.agentSessionDefaults ?? {}
50
+ if (scope === 'global') {
51
+ await patchPlatformPreferences({
52
+ agentSessionDefaults: {
53
+ ...stored,
54
+ global: mergeScopeDefaultPatch(stored.global, patch),
55
+ },
56
+ })
57
+ return
58
+ }
59
+ if (!appSlug) return
60
+ const apps = { ...(stored.apps ?? {}) }
61
+ apps[appSlug] = mergeScopeDefaultPatch(apps[appSlug], patch)
62
+ await patchPlatformPreferences({
63
+ agentSessionDefaults: { ...stored, apps },
64
+ })
65
+ }
@@ -89,12 +89,12 @@ export interface PlatformAgentSessionToolPolicy {
89
89
  }
90
90
 
91
91
  export interface PlatformAgentSessionConfig {
92
- modelId: string
92
+ modelId?: string
93
93
  /** Appended after the app's agents.md base prompt. */
94
94
  sessionPrompt?: string
95
95
  /** Full system prompt override. When set, replaces agents.md and sessionPrompt. */
96
96
  systemPrompt?: string
97
- toolPolicy?: PlatformAgentSessionToolPolicy
97
+ toolPolicy?: Partial<PlatformAgentSessionToolPolicy>
98
98
  }
99
99
 
100
100
  export interface PlatformAgentSessionMetadata {
@@ -199,6 +199,40 @@ export interface PlatformReadAgentJobRequest {
199
199
  jobId: string
200
200
  }
201
201
 
202
+ export type PlatformAgentDelegationStatus =
203
+ | 'pending'
204
+ | 'running'
205
+ | 'completed'
206
+ | 'failed'
207
+ | 'cancelled'
208
+
209
+ export interface PlatformAgentDelegationRecord {
210
+ version: 1
211
+ delegationId: string
212
+ parentJobId?: string
213
+ parentScope?: PlatformAgentSessionScope
214
+ parentAppSlug?: string
215
+ parentSessionId: string
216
+ parentRunId: string
217
+ parentToolCallId: string
218
+ childSessionId: string
219
+ childAppSlug: string
220
+ childJobId?: string
221
+ status: PlatformAgentDelegationStatus
222
+ resultJson?: unknown
223
+ error?: string
224
+ createdAt: string
225
+ updatedAt: string
226
+ }
227
+
228
+ export interface PlatformListAgentDelegationsRequest {
229
+ parentJobId?: string
230
+ childJobId?: string
231
+ parentSessionId?: string
232
+ childSessionId?: string
233
+ status?: PlatformAgentDelegationStatus
234
+ }
235
+
202
236
  export interface PlatformCancelAgentJobRequest
203
237
  extends PlatformReadAgentJobRequest {
204
238
  reason?: string
@@ -23,8 +23,10 @@ import type {
23
23
  PlatformCreateAgentSessionRequest,
24
24
  PlatformCreateAgentJobRequest,
25
25
  PlatformCancelAgentJobRequest,
26
+ PlatformAgentDelegationRecord,
26
27
  PlatformAgentJobRecord,
27
28
  PlatformListAgentSessionsRequest,
29
+ PlatformListAgentDelegationsRequest,
28
30
  PlatformListAgentJobsRequest,
29
31
  PlatformReadAgentSessionRequest,
30
32
  PlatformReadAgentJobRequest,
@@ -71,6 +73,10 @@ export function runAgentJobNow(
71
73
  request: PlatformReadAgentJobRequest,
72
74
  ): Promise<PlatformAgentJobRecord>
73
75
 
76
+ export function listAgentDelegations(
77
+ request?: PlatformListAgentDelegationsRequest,
78
+ ): Promise<PlatformAgentDelegationRecord[]>
79
+
74
80
  export function sendAgentSessionMessage(
75
81
  request: PlatformSendAgentSessionMessageRequest,
76
82
  ): Promise<PlatformAgentSessionDriveResult>
@@ -78,6 +78,16 @@ export async function runAgentJobNow(request) {
78
78
  return bridge.call(PLATFORM_BRIDGE_METHODS.AGENTS_JOBS_RUN_NOW, [request])
79
79
  }
80
80
 
81
+ /**
82
+ * @param {import('./agentTypes.js').PlatformListAgentDelegationsRequest} [request]
83
+ * @returns {Promise<import('./agentTypes.js').PlatformAgentDelegationRecord[]>}
84
+ */
85
+ export async function listAgentDelegations(request = {}) {
86
+ return bridge.call(PLATFORM_BRIDGE_METHODS.AGENTS_DELEGATIONS_LIST, [
87
+ request,
88
+ ])
89
+ }
90
+
81
91
  /**
82
92
  * Send a user message on a session; shell drives the run until awaiting user,
83
93
  * manual pending tools, or terminal state.
@@ -8,6 +8,7 @@ export {
8
8
  createAgentSession,
9
9
  deleteAgentSession,
10
10
  interruptAgentSession,
11
+ listAgentDelegations,
11
12
  listAgentJobs,
12
13
  listAgentSessions,
13
14
  onAgentRunEvent,
@@ -29,6 +30,8 @@ export {
29
30
  export type {
30
31
  PlatformAgentMessage,
31
32
  PlatformAgentMessageAttachment,
33
+ PlatformAgentDelegationRecord,
34
+ PlatformAgentDelegationStatus,
32
35
  PlatformAgentJobRecord,
33
36
  PlatformAgentJobSchedule,
34
37
  PlatformAgentJobStatus,
@@ -47,6 +50,7 @@ export type {
47
50
  PlatformCreateAgentSessionRequest,
48
51
  PlatformCreateAgentJobRequest,
49
52
  PlatformListAgentSessionsRequest,
53
+ PlatformListAgentDelegationsRequest,
50
54
  PlatformListAgentJobsRequest,
51
55
  PlatformReadAgentJobRequest,
52
56
  PlatformReadAgentSessionRequest,
@@ -0,0 +1,62 @@
1
+ export interface CredentialStorageStatus {
2
+ encryptionAvailable: boolean
3
+ backend: string | null
4
+ secure: boolean
5
+ warning?: string
6
+ }
7
+
8
+ export interface CredentialEntryStatus {
9
+ id: string
10
+ label: string
11
+ description?: string
12
+ configured: boolean
13
+ }
14
+
15
+ /** Redacted OAuth connection state — never contains secret material. */
16
+ export interface OAuthCredentialStatus {
17
+ id: string
18
+ kind: 'oauth'
19
+ provider: 'google'
20
+ configured: boolean
21
+ connected: boolean
22
+ email?: string
23
+ scopes: string[]
24
+ needsReconnect: boolean
25
+ updatedAt?: string
26
+ }
27
+
28
+ export function readCredentialStorageStatus(): Promise<CredentialStorageStatus>
29
+
30
+ export function listCredentials(): Promise<CredentialEntryStatus[]>
31
+
32
+ export function setCredential(request: {
33
+ id: string
34
+ value: string
35
+ }): Promise<{ ok: true }>
36
+
37
+ export function deleteCredential(request: { id: string }): Promise<{ ok: true }>
38
+
39
+ export function readCredentialStatus(request: {
40
+ id: string
41
+ }): Promise<OAuthCredentialStatus>
42
+
43
+ export function setCredentialOAuthConfig(request: {
44
+ id: string
45
+ clientId: string
46
+ clientSecret: string
47
+ scopes?: string[]
48
+ }): Promise<{ ok: true }>
49
+
50
+ export function connectCredentialOAuth(request: {
51
+ id: string
52
+ scopes?: string[]
53
+ }): Promise<{ email?: string }>
54
+
55
+ export function disconnectCredential(request: {
56
+ id: string
57
+ forget?: boolean
58
+ }): Promise<{ ok: true }>
59
+
60
+ export function readCredentialOAuthAccessToken(request: {
61
+ id: string
62
+ }): Promise<{ accessToken: string; expiresAt: number }>
@@ -20,3 +20,56 @@ export async function setCredential(request) {
20
20
  export async function deleteCredential(request) {
21
21
  return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_DELETE, [request])
22
22
  }
23
+
24
+ /**
25
+ * Redacted OAuth connection state — the only credential state apps may read.
26
+ * @param {{ id: string }} request
27
+ * @returns {Promise<{ id: string, configured: boolean, connected: boolean, email?: string, scopes: string[], needsReconnect: boolean, updatedAt?: string }>}
28
+ */
29
+ export async function readCredentialStatus(request) {
30
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_STATUS, [request])
31
+ }
32
+
33
+ /**
34
+ * Write-only OAuth client config; the secret is never echoed back.
35
+ * @param {{ id: string, clientId: string, clientSecret: string, scopes?: string[] }} request
36
+ * @returns {Promise<{ ok: true }>}
37
+ */
38
+ export async function setCredentialOAuthConfig(request) {
39
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_SET_OAUTH_CONFIG, [
40
+ request,
41
+ ])
42
+ }
43
+
44
+ /**
45
+ * Runs the full OAuth flow in the shell (browser + loopback + token
46
+ * exchange); no token or secret ever reaches the caller.
47
+ * @param {{ id: string, scopes?: string[] }} request
48
+ * @returns {Promise<{ email?: string }>}
49
+ */
50
+ export async function connectCredentialOAuth(request) {
51
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_CONNECT_OAUTH, [
52
+ request,
53
+ ])
54
+ }
55
+
56
+ /**
57
+ * Clears tokens + identity; pass `forget: true` to also drop the client config.
58
+ * @param {{ id: string, forget?: boolean }} request
59
+ * @returns {Promise<{ ok: true }>}
60
+ */
61
+ export async function disconnectCredential(request) {
62
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_DISCONNECT, [request])
63
+ }
64
+
65
+ /**
66
+ * Short-lived access token for providers that call the API directly.
67
+ * Refreshed transparently in the shell; refresh tokens never cross the bridge.
68
+ * @param {{ id: string }} request
69
+ * @returns {Promise<{ accessToken: string, expiresAt: number }>}
70
+ */
71
+ export async function readCredentialOAuthAccessToken(request) {
72
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_OAUTH_ACCESS_TOKEN, [
73
+ request,
74
+ ])
75
+ }
@@ -8,6 +8,7 @@ export declare const PLATFORM_BRIDGE_EVENTS: {
8
8
  readonly DEBUG_TEST: 'debug.test'
9
9
  readonly SCHEDULE_FIRE: 'schedule.fire'
10
10
  readonly WINDOW_CAMERA_SHORTCUT: 'window.cameraShortcut'
11
+ readonly OPERATIONS_CHANGED: 'operations.changed'
11
12
  }
12
13
 
13
14
  export type PlatformBridgeEventName =
@@ -9,4 +9,5 @@ export const PLATFORM_BRIDGE_EVENTS = {
9
9
  DEBUG_TEST: 'debug.test',
10
10
  SCHEDULE_FIRE: 'schedule.fire',
11
11
  WINDOW_CAMERA_SHORTCUT: 'window.cameraShortcut',
12
+ OPERATIONS_CHANGED: 'operations.changed',
12
13
  }