@athenaintel/react 0.23.3 → 0.24.0
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.
- package/README.md +6 -0
- package/dist/assets/AssetPanel.cjs +1 -1
- package/dist/assets/AssetPanel.cjs.map +1 -1
- package/dist/assets/AssetPanel.js +1 -1
- package/dist/assets/AssetPanel.js.map +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
- package/dist/assets/AthenaAssetEmbed.js +1 -1
- package/dist/assets/AthenaAssetEmbed.js.map +1 -1
- package/dist/chat/AthenaChat.cjs +15 -23
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +9 -4
- package/dist/chat/AthenaChat.js +15 -23
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireApprovalCard.cjs +3 -18
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.js +3 -18
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/StatewireClientToolBridge.cjs +2 -16
- package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
- package/dist/chat/StatewireClientToolBridge.js +2 -16
- package/dist/chat/StatewireClientToolBridge.js.map +1 -1
- package/dist/chat/StatewireQueuedMessages.cjs +5 -16
- package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
- package/dist/chat/StatewireQueuedMessages.js +5 -16
- package/dist/chat/StatewireQueuedMessages.js.map +1 -1
- package/dist/chat/WaitingOnYouBanner.cjs +3 -18
- package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
- package/dist/chat/WaitingOnYouBanner.js +3 -18
- package/dist/chat/WaitingOnYouBanner.js.map +1 -1
- package/dist/chat/run-activity.cjs +0 -16
- package/dist/chat/run-activity.cjs.map +1 -1
- package/dist/chat/run-activity.js +0 -16
- package/dist/chat/run-activity.js.map +1 -1
- package/dist/chat/statewire-client-tools.cjs +1 -17
- package/dist/chat/statewire-client-tools.cjs.map +1 -1
- package/dist/chat/statewire-client-tools.js +1 -17
- package/dist/chat/statewire-client-tools.js.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.cjs +0 -16
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.js +0 -16
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/tiptap-text.cjs +1 -17
- package/dist/chat/tiptap-text.cjs.map +1 -1
- package/dist/chat/tiptap-text.js +1 -17
- package/dist/chat/tiptap-text.js.map +1 -1
- package/dist/chat/use-athena-run-live.cjs +0 -15
- package/dist/chat/use-athena-run-live.cjs.map +1 -1
- package/dist/chat/use-athena-run-live.js +0 -15
- package/dist/chat/use-athena-run-live.js.map +1 -1
- package/dist/chat/use-statewire-approval-request.cjs +4 -16
- package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
- package/dist/chat/use-statewire-approval-request.d.ts +2 -0
- package/dist/chat/use-statewire-approval-request.js +4 -16
- package/dist/chat/use-statewire-approval-request.js.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.cjs +1 -16
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.js +1 -16
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/chat-ui/index.cjs +0 -16
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.js +0 -16
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.cjs +1 -16
- package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.js +1 -16
- package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +4 -6
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js +4 -6
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +2 -9
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js +2 -9
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
- package/dist/chat-ui/messages/message-components.cjs +5 -19
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.js +5 -19
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/chat-ui/messages/runtime-message.cjs +1 -16
- package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
- package/dist/chat-ui/messages/runtime-message.js +1 -16
- package/dist/chat-ui/messages/runtime-message.js.map +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.cjs +3 -15
- package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.js +3 -15
- package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.cjs +0 -16
- package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.js +0 -16
- package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
- package/dist/chat-ui/messages/wake-marker.cjs +1 -16
- package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
- package/dist/chat-ui/messages/wake-marker.js +1 -16
- package/dist/chat-ui/messages/wake-marker.js.map +1 -1
- package/dist/chat-ui/statewire.cjs +0 -16
- package/dist/chat-ui/statewire.cjs.map +1 -1
- package/dist/chat-ui/statewire.js +0 -16
- package/dist/chat-ui/statewire.js.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -17
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.js +2 -17
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/dictation/DictationStrip.cjs +2 -1
- package/dist/dictation/DictationStrip.cjs.map +1 -1
- package/dist/dictation/DictationStrip.d.ts +2 -0
- package/dist/dictation/DictationStrip.js +2 -1
- package/dist/dictation/DictationStrip.js.map +1 -1
- package/dist/dictation/dictation-engine.cjs.map +1 -1
- package/dist/dictation/dictation-engine.d.ts +8 -0
- package/dist/dictation/dictation-engine.js.map +1 -1
- package/dist/dictation/recorded-dictation-engine.cjs +153 -0
- package/dist/dictation/recorded-dictation-engine.cjs.map +1 -0
- package/dist/dictation/recorded-dictation-engine.d.ts +66 -0
- package/dist/dictation/recorded-dictation-engine.js +153 -0
- package/dist/dictation/recorded-dictation-engine.js.map +1 -0
- package/dist/dictation/use-athena-dictation-engine.cjs +64 -0
- package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -0
- package/dist/dictation/use-athena-dictation-engine.d.ts +25 -0
- package/dist/dictation/use-athena-dictation-engine.js +64 -0
- package/dist/dictation/use-athena-dictation-engine.js.map +1 -0
- package/dist/dictation/use-dictation.cjs +17 -6
- package/dist/dictation/use-dictation.cjs.map +1 -1
- package/dist/dictation/use-dictation.d.ts +20 -4
- package/dist/dictation/use-dictation.js +17 -6
- package/dist/dictation/use-dictation.js.map +1 -1
- package/dist/index.cjs +28 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +10 -21
- package/dist/index.js.map +1 -1
- package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
- package/dist/packages/agent-runtime/src/errors.cjs +18 -0
- package/dist/packages/agent-runtime/src/errors.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/errors.js +19 -1
- package/dist/packages/agent-runtime/src/errors.js.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +1 -0
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +1 -0
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.cjs +61 -12
- package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.js +61 -12
- package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +49 -0
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +49 -0
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
- package/dist/packages/agent-runtime/src/thread-status.js +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.cjs +1 -37
- package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.js +1 -37
- package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/packages/react/package.json.cjs +1 -1
- package/dist/packages/react/package.json.js +1 -1
- package/dist/provider/AthenaProvider.cjs +3 -17
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.js +3 -17
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/runtime/statewire-lifecycle.cjs +0 -16
- package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
- package/dist/runtime/statewire-lifecycle.js +0 -16
- package/dist/runtime/statewire-lifecycle.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +3 -17
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +3 -17
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/threads/statewire-thread-list.cjs +4 -17
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.js +4 -17
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/threads/use-rest-conversation-history.cjs +2 -16
- package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
- package/dist/threads/use-rest-conversation-history.js +2 -16
- package/dist/threads/use-rest-conversation-history.js.map +1 -1
- package/dist/threads/use-statewire-conversation-history.cjs +2 -16
- package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
- package/dist/threads/use-statewire-conversation-history.js +2 -16
- package/dist/threads/use-statewire-conversation-history.js.map +1 -1
- package/dist/toolkits/index.cjs +0 -7
- package/dist/toolkits/index.cjs.map +1 -1
- package/dist/toolkits/index.d.ts +0 -2
- package/dist/toolkits/index.js +0 -7
- package/dist/toolkits/index.js.map +1 -1
- package/dist/tools/index.cjs +4 -20
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +1 -17
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/misc-tool-uis.cjs +2 -17
- package/dist/tools/misc-tool-uis.cjs.map +1 -1
- package/dist/tools/misc-tool-uis.js +2 -17
- package/dist/tools/misc-tool-uis.js.map +1 -1
- package/dist/tools/primitives.cjs +1 -16
- package/dist/tools/primitives.cjs.map +1 -1
- package/dist/tools/primitives.js +1 -16
- package/dist/tools/primitives.js.map +1 -1
- package/dist/tools/tool-fallback.cjs +2 -17
- package/dist/tools/tool-fallback.cjs.map +1 -1
- package/dist/tools/tool-fallback.js +2 -17
- package/dist/tools/tool-fallback.js.map +1 -1
- package/dist/tools/voice-update-tool-ui.cjs +1 -17
- package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
- package/dist/tools/voice-update-tool-ui.js +1 -17
- package/dist/tools/voice-update-tool-ui.js.map +1 -1
- package/package.json +1 -1
- package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +0 -32
- package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +0 -1
- package/dist/node_modules/qrcode-generator/dist/qrcode.js +0 -31
- package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/errors.cjs +0 -44
- package/dist/node_modules/zod/v4/classic/errors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/errors.js +0 -44
- package/dist/node_modules/zod/v4/classic/errors.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/iso.cjs +0 -43
- package/dist/node_modules/zod/v4/classic/iso.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/iso.js +0 -43
- package/dist/node_modules/zod/v4/classic/iso.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/parse.cjs +0 -29
- package/dist/node_modules/zod/v4/classic/parse.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/parse.js +0 -29
- package/dist/node_modules/zod/v4/classic/parse.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/schemas.cjs +0 -871
- package/dist/node_modules/zod/v4/classic/schemas.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/schemas.js +0 -871
- package/dist/node_modules/zod/v4/classic/schemas.js.map +0 -1
- package/dist/node_modules/zod/v4/core/api.cjs +0 -570
- package/dist/node_modules/zod/v4/core/api.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/api.js +0 -570
- package/dist/node_modules/zod/v4/core/api.js.map +0 -1
- package/dist/node_modules/zod/v4/core/checks.cjs +0 -411
- package/dist/node_modules/zod/v4/core/checks.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/checks.js +0 -411
- package/dist/node_modules/zod/v4/core/checks.js.map +0 -1
- package/dist/node_modules/zod/v4/core/core.cjs +0 -77
- package/dist/node_modules/zod/v4/core/core.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/core.js +0 -77
- package/dist/node_modules/zod/v4/core/core.js.map +0 -1
- package/dist/node_modules/zod/v4/core/doc.cjs +0 -38
- package/dist/node_modules/zod/v4/core/doc.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/doc.js +0 -38
- package/dist/node_modules/zod/v4/core/doc.js.map +0 -1
- package/dist/node_modules/zod/v4/core/errors.cjs +0 -76
- package/dist/node_modules/zod/v4/core/errors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/errors.js +0 -76
- package/dist/node_modules/zod/v4/core/errors.js.map +0 -1
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +0 -312
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/json-schema-processors.js +0 -312
- package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +0 -1
- package/dist/node_modules/zod/v4/core/parse.cjs +0 -96
- package/dist/node_modules/zod/v4/core/parse.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/parse.js +0 -96
- package/dist/node_modules/zod/v4/core/parse.js.map +0 -1
- package/dist/node_modules/zod/v4/core/regexes.cjs +0 -86
- package/dist/node_modules/zod/v4/core/regexes.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/regexes.js +0 -86
- package/dist/node_modules/zod/v4/core/regexes.js.map +0 -1
- package/dist/node_modules/zod/v4/core/registries.cjs +0 -52
- package/dist/node_modules/zod/v4/core/registries.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/registries.js +0 -52
- package/dist/node_modules/zod/v4/core/registries.js.map +0 -1
- package/dist/node_modules/zod/v4/core/schemas.cjs +0 -1412
- package/dist/node_modules/zod/v4/core/schemas.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/schemas.js +0 -1413
- package/dist/node_modules/zod/v4/core/schemas.js.map +0 -1
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs +0 -370
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/to-json-schema.js +0 -370
- package/dist/node_modules/zod/v4/core/to-json-schema.js.map +0 -1
- package/dist/node_modules/zod/v4/core/util.cjs +0 -444
- package/dist/node_modules/zod/v4/core/util.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/util.js +0 -444
- package/dist/node_modules/zod/v4/core/util.js.map +0 -1
- package/dist/node_modules/zod/v4/core/versions.cjs +0 -9
- package/dist/node_modules/zod/v4/core/versions.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/versions.js +0 -9
- package/dist/node_modules/zod/v4/core/versions.js.map +0 -1
- package/dist/packages/agent-runtime/src/thread-handoff.cjs +0 -85
- package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/thread-handoff.js +0 -85
- package/dist/packages/agent-runtime/src/thread-handoff.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"thread-handoff.cjs","sources":["../../../../../agent-runtime/src/thread-handoff.ts"],"sourcesContent":["/**\n * Cross-surface \"Continue in…\" thread handoff — the client half of agora's\n * `/api/handoffs` protocol shared by Olympus (Spaces) and Athena in Chrome.\n *\n * A handoff never carries conversation content. The server binds an opaque\n * `handoff_id` to the durable thread, and the destination surface (mobile app,\n * Office add-in) claims that id and acknowledges once it owns the thread. The\n * source surface closes its own copilot only after that acknowledgement.\n */\nimport qrcode from 'qrcode-generator';\nimport { z } from 'zod';\n\nexport const HANDOFF_DESTINATIONS = ['chrome', 'mobile', 'word', 'powerpoint', 'excel'] as const;\nexport type HandoffDestination = (typeof HANDOFF_DESTINATIONS)[number];\n\nexport const OFFICE_HANDOFF_DESTINATIONS = ['word', 'powerpoint', 'excel'] as const;\nexport type OfficeHandoffDestination = (typeof OFFICE_HANDOFF_DESTINATIONS)[number];\n\n/** Destinations agora brokers; Chrome is launched page-side by the extension. */\nexport const SERVER_HANDOFF_DESTINATIONS = ['mobile', ...OFFICE_HANDOFF_DESTINATIONS] as const;\nexport type ServerHandoffDestination = (typeof SERVER_HANDOFF_DESTINATIONS)[number];\n\nexport type HandoffSource = 'spaces' | 'chrome' | 'mobile' | 'word' | 'powerpoint' | 'excel';\n\nexport const handoffEnvironmentSchema = z.enum(['production', 'staging', 'localhost']);\nexport type HandoffEnvironment = z.infer<typeof handoffEnvironmentSchema>;\n\nexport const handoffStateSchema = z.enum(['issued', 'launched', 'claimed', 'acknowledged', 'failed']);\nexport type HandoffState = z.infer<typeof handoffStateSchema>;\n\nexport const handoffReadinessStateSchema = z.enum([\n 'unsupported',\n 'unknown',\n 'not_registered',\n 'registered',\n 'stale',\n]);\nexport type HandoffReadinessState = z.infer<typeof handoffReadinessStateSchema>;\n\nconst handoffDestinationSchema = z.enum(HANDOFF_DESTINATIONS);\n\nexport const destinationReadinessSchema = z.object({\n destination: handoffDestinationSchema,\n state: handoffReadinessStateSchema,\n last_seen_at: z.string().nullable().optional(),\n label: z.string().nullable().optional(),\n marketplace_url: z.string().nullable().optional(),\n});\nexport type DestinationReadiness = z.infer<typeof destinationReadinessSchema>;\n\nexport const handoffReadinessResponseSchema = z.object({\n environment: handoffEnvironmentSchema,\n destinations: z.array(destinationReadinessSchema),\n});\nexport type HandoffReadinessResponse = z.infer<typeof handoffReadinessResponseSchema>;\n\nexport const threadHandoffViewSchema = z.object({\n handoff_id: z.string(),\n thread_id: z.string(),\n session_asset_id: z.string().nullable(),\n source: z.string(),\n destination: handoffDestinationSchema,\n environment: handoffEnvironmentSchema,\n state: handoffStateSchema,\n issued_at: z.string(),\n expires_at: z.string(),\n claimed_at: z.string().nullable(),\n acknowledged_at: z.string().nullable(),\n failure_reason: z.string().nullable(),\n push_delivered: z.boolean().nullable(),\n});\nexport type ThreadHandoffView = z.infer<typeof threadHandoffViewSchema>;\n\nexport const mobileHandoffLaunchSchema = z.object({\n kind: z.literal('mobile'),\n push_attempted: z.boolean(),\n push_delivered: z.boolean(),\n device_count: z.number(),\n /** Universal/App Link (or browser URL) carrying only the opaque handoff id. */\n link_url: z.string(),\n /** Custom-scheme fallback for browsers that do not honour app links. */\n scheme_url: z.string(),\n});\nexport type MobileHandoffLaunch = z.infer<typeof mobileHandoffLaunchSchema>;\n\nexport const officeHandoffLaunchSchema = z.object({\n kind: z.literal('office'),\n host: z.enum(OFFICE_HANDOFF_DESTINATIONS),\n /** `ms-word:nft|u|<template>` — opens a blank document from the template. */\n desktop_url: z.string(),\n template_url: z.string(),\n /** Office on the web: blank document, Athena opened from the ribbon. */\n web_url: z.string(),\n marketplace_url: z.string().nullable(),\n});\nexport type OfficeHandoffLaunch = z.infer<typeof officeHandoffLaunchSchema>;\n\nexport const chromeHandoffLaunchSchema = z.object({ kind: z.literal('chrome') });\n\nexport const handoffLaunchSchema = z.discriminatedUnion('kind', [\n mobileHandoffLaunchSchema,\n officeHandoffLaunchSchema,\n chromeHandoffLaunchSchema,\n]);\nexport type HandoffLaunch = z.infer<typeof handoffLaunchSchema>;\n\nexport const createThreadHandoffResponseSchema = z.object({\n handoff: threadHandoffViewSchema,\n launch: handoffLaunchSchema,\n});\nexport type CreateThreadHandoffResponse = z.infer<typeof createThreadHandoffResponseSchema>;\n\nexport interface CreateThreadHandoffInput {\n threadId: string;\n destination: ServerHandoffDestination;\n source: HandoffSource;\n}\n\n/**\n * Surface-specific HTTP adapter. Olympus binds it to `apiClient`; the\n * extension binds it to its authenticated fetch. Both must resolve (not\n * reject) with parsed responses so the flow can attribute failures.\n */\nexport interface ThreadHandoffTransport {\n getReadiness: (options: { signal?: AbortSignal }) => Promise<HandoffReadinessResponse>;\n createHandoff: (input: CreateThreadHandoffInput) => Promise<CreateThreadHandoffResponse>;\n getHandoff: (options: { handoffId: string; signal?: AbortSignal }) => Promise<ThreadHandoffView>;\n}\n\nexport const HANDOFF_DESTINATION_LABELS: Record<HandoffDestination, string> = {\n chrome: 'Google Chrome',\n mobile: 'Athena Mobile',\n word: 'Microsoft Word',\n powerpoint: 'Microsoft PowerPoint',\n excel: 'Microsoft Excel',\n};\n\nexport const isOfficeHandoffDestination = (\n destination: HandoffDestination\n): destination is OfficeHandoffDestination =>\n (OFFICE_HANDOFF_DESTINATIONS as readonly string[]).includes(destination);\n\nexport const isServerHandoffDestination = (\n destination: HandoffDestination\n): destination is ServerHandoffDestination => destination !== 'chrome';\n\n/** Index a readiness response by destination; missing rows read as `unknown`. */\nexport const readinessByDestination = (\n response: HandoffReadinessResponse | null | undefined\n): Record<HandoffDestination, DestinationReadiness> => {\n const result = {} as Record<HandoffDestination, DestinationReadiness>;\n for (const destination of HANDOFF_DESTINATIONS) {\n result[destination] = { destination, state: 'unknown' };\n }\n for (const row of response?.destinations ?? []) {\n result[row.destination] = row;\n }\n return result;\n};\n\n/**\n * How a mobile handoff should be presented before the phone answers.\n *\n * - `app`: a fresh push token exists — the phone gets a notification and the\n * QR is only offered as a secondary path.\n * - `qr`: no usable device is known — lead with the QR / link.\n */\nexport type MobileLaunchMode = 'app' | 'qr';\n\nexport const selectMobileLaunchMode = (readiness: DestinationReadiness | null | undefined): MobileLaunchMode =>\n readiness?.state === 'registered' ? 'app' : 'qr';\n\n/**\n * What a \"Continue in…\" row should say for a destination given its readiness.\n * Shared so Spaces and the extension render identical copy.\n */\nexport interface HandoffDestinationPresentation {\n destination: HandoffDestination;\n label: string;\n /** Secondary line under the label; `null` when the row needs no caveat. */\n detail: string | null;\n /**\n * `install` rows do not create a handoff — they point at install help;\n * `unavailable` rows are disabled.\n */\n action: 'handoff' | 'install' | 'unavailable';\n}\n\nexport const presentHandoffDestination = ({\n destination,\n readiness,\n}: {\n destination: HandoffDestination;\n readiness: DestinationReadiness | null | undefined;\n}): HandoffDestinationPresentation => {\n const label = HANDOFF_DESTINATION_LABELS[destination];\n const state = readiness?.state ?? 'unknown';\n if (destination === 'chrome') {\n return { destination, label, detail: null, action: 'handoff' };\n }\n if (state === 'unsupported') {\n return { destination, label, detail: 'Not available in this environment', action: 'unavailable' };\n }\n if (destination === 'mobile') {\n switch (state) {\n case 'registered':\n return {\n destination,\n label,\n detail: readiness?.label ? `Notify ${readiness.label}` : 'Notify your phone',\n action: 'handoff',\n };\n case 'stale':\n return {\n destination,\n label,\n detail: readiness?.label\n ? `${readiness.label} hasn't checked in recently — scan a QR code`\n : 'Scan a QR code',\n action: 'handoff',\n };\n default:\n return { destination, label, detail: 'Scan a QR code', action: 'handoff' };\n }\n }\n switch (state) {\n case 'registered':\n return { destination, label, detail: 'Opens a blank document', action: 'handoff' };\n case 'stale':\n return {\n destination,\n label,\n detail: 'Opens a blank document — add-in last seen a while ago',\n action: 'handoff',\n };\n case 'not_registered':\n // Presence is only recorded once the pane has run, so \"not detected\" can\n // be a false negative; keep the handoff available unless a store exists.\n return {\n destination,\n label,\n detail: 'Athena add-in not detected',\n action: readiness?.marketplace_url ? 'install' : 'handoff',\n };\n default:\n return { destination, label, detail: 'Opens a blank document', action: 'handoff' };\n }\n};\n\n/**\n * QR symbol for a handoff link as a single SVG path in module units, so any\n * surface can render `<svg viewBox=\"0 0 {size} {size}\"><path d={path} /></svg>`\n * without a canvas or an image request.\n */\nexport const buildHandoffQrSvg = ({ text }: { text: string }): { size: number; path: string } => {\n const symbol = qrcode(0, 'M');\n symbol.addData(text);\n symbol.make();\n const size: number = symbol.getModuleCount();\n const segments: string[] = [];\n for (let row = 0; row < size; row += 1) {\n let runStart = -1;\n for (let col = 0; col <= size; col += 1) {\n const dark: boolean = col < size && symbol.isDark(row, col);\n if (dark && runStart === -1) {\n runStart = col;\n } else if (!dark && runStart !== -1) {\n segments.push(`M${runStart} ${row}h${col - runStart}v1h-${col - runStart}z`);\n runStart = -1;\n }\n }\n }\n return { size, path: segments.join('') };\n};\n\nexport type HandoffOutcome =\n | { status: 'acknowledged'; handoff: ThreadHandoffView }\n | { status: 'failed'; handoff: ThreadHandoffView | null; reason: string }\n | { status: 'timeout'; handoff: ThreadHandoffView | null }\n | { status: 'cancelled'; handoff: ThreadHandoffView | null };\n\nexport const isTerminalHandoffState = (state: HandoffState): boolean =>\n state === 'acknowledged' || state === 'failed';\n\nexport interface WaitForHandoffOutcomeOptions {\n transport: Pick<ThreadHandoffTransport, 'getHandoff'>;\n handoffId: string;\n /** Wall-clock budget; mobile pushes typically land in seconds, Office launches in tens of seconds. */\n timeoutMs: number;\n pollIntervalMs?: number;\n signal?: AbortSignal;\n onUpdate?: (handoff: ThreadHandoffView) => void;\n /** Injected for tests. */\n now?: () => number;\n sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;\n}\n\nconst defaultSleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n function onAbort() {\n clearTimeout(timer);\n resolve();\n }\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/**\n * Poll one handoff until the destination acknowledges, the server marks it\n * failed, the budget elapses, or the caller aborts. Each request is bound to\n * the remaining budget so a stalled response cannot outlive the deadline.\n * Retryable transport errors are retried within the budget; permanent ones\n * (auth, wrong deployment, malformed payload) fail immediately with their\n * actionable message. The last known view is reported.\n */\nexport async function waitForHandoffOutcome({\n transport,\n handoffId,\n timeoutMs,\n pollIntervalMs = 1500,\n signal,\n onUpdate,\n now = () => Date.now(),\n sleep = defaultSleep,\n}: WaitForHandoffOutcomeOptions): Promise<HandoffOutcome> {\n const deadline = now() + timeoutMs;\n let lastKnown: ThreadHandoffView | null = null;\n while (true) {\n if (signal?.aborted) {\n return { status: 'cancelled', handoff: lastKnown };\n }\n const budgetMs: number = deadline - now();\n if (budgetMs <= 0) {\n return { status: 'timeout', handoff: lastKnown };\n }\n const poll = new AbortController();\n const abortPoll = () => poll.abort();\n const pollDeadline = setTimeout(abortPoll, budgetMs);\n signal?.addEventListener('abort', abortPoll, { once: true });\n try {\n const view = await transport.getHandoff({ handoffId, signal: poll.signal });\n lastKnown = view;\n onUpdate?.(view);\n if (view.state === 'acknowledged') {\n return { status: 'acknowledged', handoff: view };\n }\n if (view.state === 'failed') {\n return {\n status: 'failed',\n handoff: view,\n reason: view.failure_reason ?? 'The destination could not open this conversation.',\n };\n }\n if (now() >= Date.parse(view.expires_at)) {\n return { status: 'timeout', handoff: view };\n }\n } catch (error) {\n if (signal?.aborted) {\n return { status: 'cancelled', handoff: lastKnown };\n }\n if (poll.signal.aborted) {\n return { status: 'timeout', handoff: lastKnown };\n }\n switch (classifyHandoffTransportError(error)) {\n case 'gone':\n return {\n status: 'failed',\n handoff: lastKnown,\n reason: 'This handoff expired before the destination opened it.',\n };\n case 'permanent':\n return { status: 'failed', handoff: lastKnown, reason: describeHandoffTransportError(error) };\n case 'retryable':\n break;\n }\n } finally {\n clearTimeout(pollDeadline);\n signal?.removeEventListener('abort', abortPoll);\n }\n const remainingMs: number = deadline - now();\n if (remainingMs <= 0) {\n return { status: 'timeout', handoff: lastKnown };\n }\n await sleep(Math.min(pollIntervalMs, remainingMs), signal);\n if (now() >= deadline) {\n return { status: 'timeout', handoff: lastKnown };\n }\n }\n}\n\n/** Error raised by a transport when the server no longer knows the handoff (404). */\nexport class HandoffGoneError extends Error {\n constructor(handoffId: string) {\n super(`Handoff ${handoffId} is no longer available`);\n this.name = 'HandoffGoneError';\n }\n}\n\nexport const isHandoffGoneError = (error: unknown): error is HandoffGoneError =>\n error instanceof HandoffGoneError ||\n (error instanceof Error && error.name === 'HandoffGoneError');\n\n/**\n * Transport failure that polling the same handoff can never recover from:\n * the session is signed out, the token belongs to another deployment, the\n * server rejected the request, or the payload did not match the schema.\n */\nexport class HandoffPermanentError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'HandoffPermanentError';\n }\n}\n\nexport const isHandoffPermanentError = (error: unknown): error is HandoffPermanentError =>\n error instanceof HandoffPermanentError ||\n (error instanceof Error && error.name === 'HandoffPermanentError');\n\nexport type HandoffTransportErrorKind = 'gone' | 'permanent' | 'retryable';\n\n/**\n * Shared classification for both transports: only network failures and\n * retryable server statuses keep the poll alive. Schema mismatches surface as\n * `ZodError` from either adapter and are permanent.\n */\nexport const classifyHandoffTransportError = (error: unknown): HandoffTransportErrorKind => {\n if (isHandoffGoneError(error)) return 'gone';\n if (isHandoffPermanentError(error) || error instanceof z.ZodError) return 'permanent';\n return 'retryable';\n};\n\n/** 5xx, timeouts and rate limits may clear on the next poll; any other 4xx will not. */\nexport const isRetryableHandoffHttpStatus = (status: number): boolean =>\n status >= 500 || status === 408 || status === 429;\n\n/**\n * Map a non-2xx status to the transport error the poll loop understands.\n * `detail` is the server's actionable message when it sent one.\n */\nexport const handoffHttpError = ({\n handoffId,\n status,\n detail,\n}: {\n handoffId: string;\n status: number;\n detail: string | null;\n}): Error => {\n if (status === 404) return new HandoffGoneError(handoffId);\n const message: string = detail ?? `The handoff request failed (${status}).`;\n return isRetryableHandoffHttpStatus(status) ? new Error(message) : new HandoffPermanentError(message);\n};\n\nexport const describeHandoffTransportError = (error: unknown): string => {\n if (error instanceof z.ZodError) {\n return 'The server sent an unexpected response. Refresh and try again.';\n }\n if (error instanceof Error && error.message) return error.message;\n return 'Lost track of the handoff. Try again.';\n};\n\n/** `?handoff=<id>` — the query parameter mobile-web and QR links carry. */\nexport const HANDOFF_URL_PARAM = 'handoff';\n\n/**\n * Read a handoff id from a URL. Ids are server-issued, URL-safe tokens; anything\n * else is rejected so a tampered link never reaches the claim path.\n */\nexport const parseHandoffIdParam = (value: string | null | undefined): string | null => {\n if (!value) {\n return null;\n }\n return /^[A-Za-z0-9_-]{8,128}$/.test(value) ? value : null;\n};\n\n/** Athena Mobile's custom URL scheme (`mobile-app-react-native/app.json` → `scheme`). */\nexport const MOBILE_APP_SCHEME = 'athenavisionapplication';\n\n/**\n * Custom-scheme launch for a handoff — what a phone's browser opens when the\n * Universal/App Link landed on mobile web instead of in the app. Mirrors\n * agora's `_mobile_scheme_url`; carries only the opaque handoff id.\n */\nexport const buildMobileHandoffSchemeUrl = ({ handoffId }: { handoffId: string }): string =>\n `${MOBILE_APP_SCHEME}://chat?${HANDOFF_URL_PARAM}=${encodeURIComponent(handoffId)}`;\n"],"names":["z.enum","z.object","z.string","z.array","z.boolean","z.literal","z.number","z.discriminatedUnion"],"mappings":";;;;AAYO,MAAM,uBAAuB,CAAC,UAAU,UAAU,QAAQ,cAAc,OAAO;AAG/E,MAAM,8BAA8B,CAAC,QAAQ,cAAc,OAAO;AAI9B,CAAC,UAAU,GAAG,2BAA2B;AAK7E,MAAM,2BAA2BA,QAAAA,KAAO,CAAC,cAAc,WAAW,WAAW,CAAC;AAG9E,MAAM,qBAAqBA,QAAAA,KAAO,CAAC,UAAU,YAAY,WAAW,gBAAgB,QAAQ,CAAC;AAG7F,MAAM,8BAA8BA,QAAAA,KAAO;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,MAAM,2BAA2BA,QAAAA,KAAO,oBAAoB;AAErD,MAAM,6BAA6BC,QAAAA,OAAS;AAAA,EACjD,aAAa;AAAA,EACb,OAAO;AAAA,EACP,cAAcC,QAAAA,OAAE,EAAS,SAAA,EAAW,SAAA;AAAA,EACpC,OAAOA,QAAAA,OAAE,EAAS,SAAA,EAAW,SAAA;AAAA,EAC7B,iBAAiBA,QAAAA,OAAE,EAAS,SAAA,EAAW,SAAA;AACzC,CAAC;AAG6CD,QAAAA,OAAS;AAAA,EACrD,aAAa;AAAA,EACb,cAAcE,QAAAA,MAAQ,0BAA0B;AAClD,CAAC;AAGM,MAAM,0BAA0BF,QAAAA,OAAS;AAAA,EAC9C,YAAYC,QAAAA,OAAE;AAAA,EACd,WAAWA,QAAAA,OAAE;AAAA,EACb,kBAAkBA,QAAAA,OAAE,EAAS,SAAA;AAAA,EAC7B,QAAQA,QAAAA,OAAE;AAAA,EACV,aAAa;AAAA,EACb,aAAa;AAAA,EACb,OAAO;AAAA,EACP,WAAWA,QAAAA,OAAE;AAAA,EACb,YAAYA,QAAAA,OAAE;AAAA,EACd,YAAYA,QAAAA,OAAE,EAAS,SAAA;AAAA,EACvB,iBAAiBA,QAAAA,OAAE,EAAS,SAAA;AAAA,EAC5B,gBAAgBA,QAAAA,OAAE,EAAS,SAAA;AAAA,EAC3B,gBAAgBE,QAAAA,QAAE,EAAU,SAAA;AAC9B,CAAC;AAGM,MAAM,4BAA4BH,QAAAA,OAAS;AAAA,EAChD,MAAMI,QAAAA,QAAU,QAAQ;AAAA,EACxB,gBAAgBD,QAAAA,QAAE;AAAA,EAClB,gBAAgBA,QAAAA,QAAE;AAAA,EAClB,cAAcE,QAAAA,OAAE;AAAA;AAAA,EAEhB,UAAUJ,QAAAA,OAAE;AAAA;AAAA,EAEZ,YAAYA,QAAAA,OAAE;AAChB,CAAC;AAGM,MAAM,4BAA4BD,QAAAA,OAAS;AAAA,EAChD,MAAMI,QAAAA,QAAU,QAAQ;AAAA,EACxB,MAAML,QAAAA,KAAO,2BAA2B;AAAA;AAAA,EAExC,aAAaE,QAAAA,OAAE;AAAA,EACf,cAAcA,QAAAA,OAAE;AAAA;AAAA,EAEhB,SAASA,QAAAA,OAAE;AAAA,EACX,iBAAiBA,QAAAA,OAAE,EAAS,SAAA;AAC9B,CAAC;AAGM,MAAM,4BAA4BD,QAAAA,OAAS,EAAE,MAAMI,QAAAA,QAAU,QAAQ,EAAA,CAAG;AAExE,MAAM,sBAAsBE,QAAAA,mBAAqB,QAAQ;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGgDN,QAAAA,OAAS;AAAA,EACxD,SAAS;AAAA,EACT,QAAQ;AACV,CAAC;;;;;;;;;;;;"}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import "../../../node_modules/qrcode-generator/dist/qrcode.js";
|
|
2
|
-
import { enum as _enum, object, string, boolean, literal, discriminatedUnion, array, number } from "../../../node_modules/zod/v4/classic/schemas.js";
|
|
3
|
-
const HANDOFF_DESTINATIONS = ["chrome", "mobile", "word", "powerpoint", "excel"];
|
|
4
|
-
const OFFICE_HANDOFF_DESTINATIONS = ["word", "powerpoint", "excel"];
|
|
5
|
-
["mobile", ...OFFICE_HANDOFF_DESTINATIONS];
|
|
6
|
-
const handoffEnvironmentSchema = _enum(["production", "staging", "localhost"]);
|
|
7
|
-
const handoffStateSchema = _enum(["issued", "launched", "claimed", "acknowledged", "failed"]);
|
|
8
|
-
const handoffReadinessStateSchema = _enum([
|
|
9
|
-
"unsupported",
|
|
10
|
-
"unknown",
|
|
11
|
-
"not_registered",
|
|
12
|
-
"registered",
|
|
13
|
-
"stale"
|
|
14
|
-
]);
|
|
15
|
-
const handoffDestinationSchema = _enum(HANDOFF_DESTINATIONS);
|
|
16
|
-
const destinationReadinessSchema = object({
|
|
17
|
-
destination: handoffDestinationSchema,
|
|
18
|
-
state: handoffReadinessStateSchema,
|
|
19
|
-
last_seen_at: string().nullable().optional(),
|
|
20
|
-
label: string().nullable().optional(),
|
|
21
|
-
marketplace_url: string().nullable().optional()
|
|
22
|
-
});
|
|
23
|
-
object({
|
|
24
|
-
environment: handoffEnvironmentSchema,
|
|
25
|
-
destinations: array(destinationReadinessSchema)
|
|
26
|
-
});
|
|
27
|
-
const threadHandoffViewSchema = object({
|
|
28
|
-
handoff_id: string(),
|
|
29
|
-
thread_id: string(),
|
|
30
|
-
session_asset_id: string().nullable(),
|
|
31
|
-
source: string(),
|
|
32
|
-
destination: handoffDestinationSchema,
|
|
33
|
-
environment: handoffEnvironmentSchema,
|
|
34
|
-
state: handoffStateSchema,
|
|
35
|
-
issued_at: string(),
|
|
36
|
-
expires_at: string(),
|
|
37
|
-
claimed_at: string().nullable(),
|
|
38
|
-
acknowledged_at: string().nullable(),
|
|
39
|
-
failure_reason: string().nullable(),
|
|
40
|
-
push_delivered: boolean().nullable()
|
|
41
|
-
});
|
|
42
|
-
const mobileHandoffLaunchSchema = object({
|
|
43
|
-
kind: literal("mobile"),
|
|
44
|
-
push_attempted: boolean(),
|
|
45
|
-
push_delivered: boolean(),
|
|
46
|
-
device_count: number(),
|
|
47
|
-
/** Universal/App Link (or browser URL) carrying only the opaque handoff id. */
|
|
48
|
-
link_url: string(),
|
|
49
|
-
/** Custom-scheme fallback for browsers that do not honour app links. */
|
|
50
|
-
scheme_url: string()
|
|
51
|
-
});
|
|
52
|
-
const officeHandoffLaunchSchema = object({
|
|
53
|
-
kind: literal("office"),
|
|
54
|
-
host: _enum(OFFICE_HANDOFF_DESTINATIONS),
|
|
55
|
-
/** `ms-word:nft|u|<template>` — opens a blank document from the template. */
|
|
56
|
-
desktop_url: string(),
|
|
57
|
-
template_url: string(),
|
|
58
|
-
/** Office on the web: blank document, Athena opened from the ribbon. */
|
|
59
|
-
web_url: string(),
|
|
60
|
-
marketplace_url: string().nullable()
|
|
61
|
-
});
|
|
62
|
-
const chromeHandoffLaunchSchema = object({ kind: literal("chrome") });
|
|
63
|
-
const handoffLaunchSchema = discriminatedUnion("kind", [
|
|
64
|
-
mobileHandoffLaunchSchema,
|
|
65
|
-
officeHandoffLaunchSchema,
|
|
66
|
-
chromeHandoffLaunchSchema
|
|
67
|
-
]);
|
|
68
|
-
object({
|
|
69
|
-
handoff: threadHandoffViewSchema,
|
|
70
|
-
launch: handoffLaunchSchema
|
|
71
|
-
});
|
|
72
|
-
export {
|
|
73
|
-
HANDOFF_DESTINATIONS,
|
|
74
|
-
OFFICE_HANDOFF_DESTINATIONS,
|
|
75
|
-
chromeHandoffLaunchSchema,
|
|
76
|
-
destinationReadinessSchema,
|
|
77
|
-
handoffEnvironmentSchema,
|
|
78
|
-
handoffLaunchSchema,
|
|
79
|
-
handoffReadinessStateSchema,
|
|
80
|
-
handoffStateSchema,
|
|
81
|
-
mobileHandoffLaunchSchema,
|
|
82
|
-
officeHandoffLaunchSchema,
|
|
83
|
-
threadHandoffViewSchema
|
|
84
|
-
};
|
|
85
|
-
//# sourceMappingURL=thread-handoff.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"thread-handoff.js","sources":["../../../../../agent-runtime/src/thread-handoff.ts"],"sourcesContent":["/**\n * Cross-surface \"Continue in…\" thread handoff — the client half of agora's\n * `/api/handoffs` protocol shared by Olympus (Spaces) and Athena in Chrome.\n *\n * A handoff never carries conversation content. The server binds an opaque\n * `handoff_id` to the durable thread, and the destination surface (mobile app,\n * Office add-in) claims that id and acknowledges once it owns the thread. The\n * source surface closes its own copilot only after that acknowledgement.\n */\nimport qrcode from 'qrcode-generator';\nimport { z } from 'zod';\n\nexport const HANDOFF_DESTINATIONS = ['chrome', 'mobile', 'word', 'powerpoint', 'excel'] as const;\nexport type HandoffDestination = (typeof HANDOFF_DESTINATIONS)[number];\n\nexport const OFFICE_HANDOFF_DESTINATIONS = ['word', 'powerpoint', 'excel'] as const;\nexport type OfficeHandoffDestination = (typeof OFFICE_HANDOFF_DESTINATIONS)[number];\n\n/** Destinations agora brokers; Chrome is launched page-side by the extension. */\nexport const SERVER_HANDOFF_DESTINATIONS = ['mobile', ...OFFICE_HANDOFF_DESTINATIONS] as const;\nexport type ServerHandoffDestination = (typeof SERVER_HANDOFF_DESTINATIONS)[number];\n\nexport type HandoffSource = 'spaces' | 'chrome' | 'mobile' | 'word' | 'powerpoint' | 'excel';\n\nexport const handoffEnvironmentSchema = z.enum(['production', 'staging', 'localhost']);\nexport type HandoffEnvironment = z.infer<typeof handoffEnvironmentSchema>;\n\nexport const handoffStateSchema = z.enum(['issued', 'launched', 'claimed', 'acknowledged', 'failed']);\nexport type HandoffState = z.infer<typeof handoffStateSchema>;\n\nexport const handoffReadinessStateSchema = z.enum([\n 'unsupported',\n 'unknown',\n 'not_registered',\n 'registered',\n 'stale',\n]);\nexport type HandoffReadinessState = z.infer<typeof handoffReadinessStateSchema>;\n\nconst handoffDestinationSchema = z.enum(HANDOFF_DESTINATIONS);\n\nexport const destinationReadinessSchema = z.object({\n destination: handoffDestinationSchema,\n state: handoffReadinessStateSchema,\n last_seen_at: z.string().nullable().optional(),\n label: z.string().nullable().optional(),\n marketplace_url: z.string().nullable().optional(),\n});\nexport type DestinationReadiness = z.infer<typeof destinationReadinessSchema>;\n\nexport const handoffReadinessResponseSchema = z.object({\n environment: handoffEnvironmentSchema,\n destinations: z.array(destinationReadinessSchema),\n});\nexport type HandoffReadinessResponse = z.infer<typeof handoffReadinessResponseSchema>;\n\nexport const threadHandoffViewSchema = z.object({\n handoff_id: z.string(),\n thread_id: z.string(),\n session_asset_id: z.string().nullable(),\n source: z.string(),\n destination: handoffDestinationSchema,\n environment: handoffEnvironmentSchema,\n state: handoffStateSchema,\n issued_at: z.string(),\n expires_at: z.string(),\n claimed_at: z.string().nullable(),\n acknowledged_at: z.string().nullable(),\n failure_reason: z.string().nullable(),\n push_delivered: z.boolean().nullable(),\n});\nexport type ThreadHandoffView = z.infer<typeof threadHandoffViewSchema>;\n\nexport const mobileHandoffLaunchSchema = z.object({\n kind: z.literal('mobile'),\n push_attempted: z.boolean(),\n push_delivered: z.boolean(),\n device_count: z.number(),\n /** Universal/App Link (or browser URL) carrying only the opaque handoff id. */\n link_url: z.string(),\n /** Custom-scheme fallback for browsers that do not honour app links. */\n scheme_url: z.string(),\n});\nexport type MobileHandoffLaunch = z.infer<typeof mobileHandoffLaunchSchema>;\n\nexport const officeHandoffLaunchSchema = z.object({\n kind: z.literal('office'),\n host: z.enum(OFFICE_HANDOFF_DESTINATIONS),\n /** `ms-word:nft|u|<template>` — opens a blank document from the template. */\n desktop_url: z.string(),\n template_url: z.string(),\n /** Office on the web: blank document, Athena opened from the ribbon. */\n web_url: z.string(),\n marketplace_url: z.string().nullable(),\n});\nexport type OfficeHandoffLaunch = z.infer<typeof officeHandoffLaunchSchema>;\n\nexport const chromeHandoffLaunchSchema = z.object({ kind: z.literal('chrome') });\n\nexport const handoffLaunchSchema = z.discriminatedUnion('kind', [\n mobileHandoffLaunchSchema,\n officeHandoffLaunchSchema,\n chromeHandoffLaunchSchema,\n]);\nexport type HandoffLaunch = z.infer<typeof handoffLaunchSchema>;\n\nexport const createThreadHandoffResponseSchema = z.object({\n handoff: threadHandoffViewSchema,\n launch: handoffLaunchSchema,\n});\nexport type CreateThreadHandoffResponse = z.infer<typeof createThreadHandoffResponseSchema>;\n\nexport interface CreateThreadHandoffInput {\n threadId: string;\n destination: ServerHandoffDestination;\n source: HandoffSource;\n}\n\n/**\n * Surface-specific HTTP adapter. Olympus binds it to `apiClient`; the\n * extension binds it to its authenticated fetch. Both must resolve (not\n * reject) with parsed responses so the flow can attribute failures.\n */\nexport interface ThreadHandoffTransport {\n getReadiness: (options: { signal?: AbortSignal }) => Promise<HandoffReadinessResponse>;\n createHandoff: (input: CreateThreadHandoffInput) => Promise<CreateThreadHandoffResponse>;\n getHandoff: (options: { handoffId: string; signal?: AbortSignal }) => Promise<ThreadHandoffView>;\n}\n\nexport const HANDOFF_DESTINATION_LABELS: Record<HandoffDestination, string> = {\n chrome: 'Google Chrome',\n mobile: 'Athena Mobile',\n word: 'Microsoft Word',\n powerpoint: 'Microsoft PowerPoint',\n excel: 'Microsoft Excel',\n};\n\nexport const isOfficeHandoffDestination = (\n destination: HandoffDestination\n): destination is OfficeHandoffDestination =>\n (OFFICE_HANDOFF_DESTINATIONS as readonly string[]).includes(destination);\n\nexport const isServerHandoffDestination = (\n destination: HandoffDestination\n): destination is ServerHandoffDestination => destination !== 'chrome';\n\n/** Index a readiness response by destination; missing rows read as `unknown`. */\nexport const readinessByDestination = (\n response: HandoffReadinessResponse | null | undefined\n): Record<HandoffDestination, DestinationReadiness> => {\n const result = {} as Record<HandoffDestination, DestinationReadiness>;\n for (const destination of HANDOFF_DESTINATIONS) {\n result[destination] = { destination, state: 'unknown' };\n }\n for (const row of response?.destinations ?? []) {\n result[row.destination] = row;\n }\n return result;\n};\n\n/**\n * How a mobile handoff should be presented before the phone answers.\n *\n * - `app`: a fresh push token exists — the phone gets a notification and the\n * QR is only offered as a secondary path.\n * - `qr`: no usable device is known — lead with the QR / link.\n */\nexport type MobileLaunchMode = 'app' | 'qr';\n\nexport const selectMobileLaunchMode = (readiness: DestinationReadiness | null | undefined): MobileLaunchMode =>\n readiness?.state === 'registered' ? 'app' : 'qr';\n\n/**\n * What a \"Continue in…\" row should say for a destination given its readiness.\n * Shared so Spaces and the extension render identical copy.\n */\nexport interface HandoffDestinationPresentation {\n destination: HandoffDestination;\n label: string;\n /** Secondary line under the label; `null` when the row needs no caveat. */\n detail: string | null;\n /**\n * `install` rows do not create a handoff — they point at install help;\n * `unavailable` rows are disabled.\n */\n action: 'handoff' | 'install' | 'unavailable';\n}\n\nexport const presentHandoffDestination = ({\n destination,\n readiness,\n}: {\n destination: HandoffDestination;\n readiness: DestinationReadiness | null | undefined;\n}): HandoffDestinationPresentation => {\n const label = HANDOFF_DESTINATION_LABELS[destination];\n const state = readiness?.state ?? 'unknown';\n if (destination === 'chrome') {\n return { destination, label, detail: null, action: 'handoff' };\n }\n if (state === 'unsupported') {\n return { destination, label, detail: 'Not available in this environment', action: 'unavailable' };\n }\n if (destination === 'mobile') {\n switch (state) {\n case 'registered':\n return {\n destination,\n label,\n detail: readiness?.label ? `Notify ${readiness.label}` : 'Notify your phone',\n action: 'handoff',\n };\n case 'stale':\n return {\n destination,\n label,\n detail: readiness?.label\n ? `${readiness.label} hasn't checked in recently — scan a QR code`\n : 'Scan a QR code',\n action: 'handoff',\n };\n default:\n return { destination, label, detail: 'Scan a QR code', action: 'handoff' };\n }\n }\n switch (state) {\n case 'registered':\n return { destination, label, detail: 'Opens a blank document', action: 'handoff' };\n case 'stale':\n return {\n destination,\n label,\n detail: 'Opens a blank document — add-in last seen a while ago',\n action: 'handoff',\n };\n case 'not_registered':\n // Presence is only recorded once the pane has run, so \"not detected\" can\n // be a false negative; keep the handoff available unless a store exists.\n return {\n destination,\n label,\n detail: 'Athena add-in not detected',\n action: readiness?.marketplace_url ? 'install' : 'handoff',\n };\n default:\n return { destination, label, detail: 'Opens a blank document', action: 'handoff' };\n }\n};\n\n/**\n * QR symbol for a handoff link as a single SVG path in module units, so any\n * surface can render `<svg viewBox=\"0 0 {size} {size}\"><path d={path} /></svg>`\n * without a canvas or an image request.\n */\nexport const buildHandoffQrSvg = ({ text }: { text: string }): { size: number; path: string } => {\n const symbol = qrcode(0, 'M');\n symbol.addData(text);\n symbol.make();\n const size: number = symbol.getModuleCount();\n const segments: string[] = [];\n for (let row = 0; row < size; row += 1) {\n let runStart = -1;\n for (let col = 0; col <= size; col += 1) {\n const dark: boolean = col < size && symbol.isDark(row, col);\n if (dark && runStart === -1) {\n runStart = col;\n } else if (!dark && runStart !== -1) {\n segments.push(`M${runStart} ${row}h${col - runStart}v1h-${col - runStart}z`);\n runStart = -1;\n }\n }\n }\n return { size, path: segments.join('') };\n};\n\nexport type HandoffOutcome =\n | { status: 'acknowledged'; handoff: ThreadHandoffView }\n | { status: 'failed'; handoff: ThreadHandoffView | null; reason: string }\n | { status: 'timeout'; handoff: ThreadHandoffView | null }\n | { status: 'cancelled'; handoff: ThreadHandoffView | null };\n\nexport const isTerminalHandoffState = (state: HandoffState): boolean =>\n state === 'acknowledged' || state === 'failed';\n\nexport interface WaitForHandoffOutcomeOptions {\n transport: Pick<ThreadHandoffTransport, 'getHandoff'>;\n handoffId: string;\n /** Wall-clock budget; mobile pushes typically land in seconds, Office launches in tens of seconds. */\n timeoutMs: number;\n pollIntervalMs?: number;\n signal?: AbortSignal;\n onUpdate?: (handoff: ThreadHandoffView) => void;\n /** Injected for tests. */\n now?: () => number;\n sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;\n}\n\nconst defaultSleep = (ms: number, signal?: AbortSignal): Promise<void> =>\n new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n function onAbort() {\n clearTimeout(timer);\n resolve();\n }\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/**\n * Poll one handoff until the destination acknowledges, the server marks it\n * failed, the budget elapses, or the caller aborts. Each request is bound to\n * the remaining budget so a stalled response cannot outlive the deadline.\n * Retryable transport errors are retried within the budget; permanent ones\n * (auth, wrong deployment, malformed payload) fail immediately with their\n * actionable message. The last known view is reported.\n */\nexport async function waitForHandoffOutcome({\n transport,\n handoffId,\n timeoutMs,\n pollIntervalMs = 1500,\n signal,\n onUpdate,\n now = () => Date.now(),\n sleep = defaultSleep,\n}: WaitForHandoffOutcomeOptions): Promise<HandoffOutcome> {\n const deadline = now() + timeoutMs;\n let lastKnown: ThreadHandoffView | null = null;\n while (true) {\n if (signal?.aborted) {\n return { status: 'cancelled', handoff: lastKnown };\n }\n const budgetMs: number = deadline - now();\n if (budgetMs <= 0) {\n return { status: 'timeout', handoff: lastKnown };\n }\n const poll = new AbortController();\n const abortPoll = () => poll.abort();\n const pollDeadline = setTimeout(abortPoll, budgetMs);\n signal?.addEventListener('abort', abortPoll, { once: true });\n try {\n const view = await transport.getHandoff({ handoffId, signal: poll.signal });\n lastKnown = view;\n onUpdate?.(view);\n if (view.state === 'acknowledged') {\n return { status: 'acknowledged', handoff: view };\n }\n if (view.state === 'failed') {\n return {\n status: 'failed',\n handoff: view,\n reason: view.failure_reason ?? 'The destination could not open this conversation.',\n };\n }\n if (now() >= Date.parse(view.expires_at)) {\n return { status: 'timeout', handoff: view };\n }\n } catch (error) {\n if (signal?.aborted) {\n return { status: 'cancelled', handoff: lastKnown };\n }\n if (poll.signal.aborted) {\n return { status: 'timeout', handoff: lastKnown };\n }\n switch (classifyHandoffTransportError(error)) {\n case 'gone':\n return {\n status: 'failed',\n handoff: lastKnown,\n reason: 'This handoff expired before the destination opened it.',\n };\n case 'permanent':\n return { status: 'failed', handoff: lastKnown, reason: describeHandoffTransportError(error) };\n case 'retryable':\n break;\n }\n } finally {\n clearTimeout(pollDeadline);\n signal?.removeEventListener('abort', abortPoll);\n }\n const remainingMs: number = deadline - now();\n if (remainingMs <= 0) {\n return { status: 'timeout', handoff: lastKnown };\n }\n await sleep(Math.min(pollIntervalMs, remainingMs), signal);\n if (now() >= deadline) {\n return { status: 'timeout', handoff: lastKnown };\n }\n }\n}\n\n/** Error raised by a transport when the server no longer knows the handoff (404). */\nexport class HandoffGoneError extends Error {\n constructor(handoffId: string) {\n super(`Handoff ${handoffId} is no longer available`);\n this.name = 'HandoffGoneError';\n }\n}\n\nexport const isHandoffGoneError = (error: unknown): error is HandoffGoneError =>\n error instanceof HandoffGoneError ||\n (error instanceof Error && error.name === 'HandoffGoneError');\n\n/**\n * Transport failure that polling the same handoff can never recover from:\n * the session is signed out, the token belongs to another deployment, the\n * server rejected the request, or the payload did not match the schema.\n */\nexport class HandoffPermanentError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'HandoffPermanentError';\n }\n}\n\nexport const isHandoffPermanentError = (error: unknown): error is HandoffPermanentError =>\n error instanceof HandoffPermanentError ||\n (error instanceof Error && error.name === 'HandoffPermanentError');\n\nexport type HandoffTransportErrorKind = 'gone' | 'permanent' | 'retryable';\n\n/**\n * Shared classification for both transports: only network failures and\n * retryable server statuses keep the poll alive. Schema mismatches surface as\n * `ZodError` from either adapter and are permanent.\n */\nexport const classifyHandoffTransportError = (error: unknown): HandoffTransportErrorKind => {\n if (isHandoffGoneError(error)) return 'gone';\n if (isHandoffPermanentError(error) || error instanceof z.ZodError) return 'permanent';\n return 'retryable';\n};\n\n/** 5xx, timeouts and rate limits may clear on the next poll; any other 4xx will not. */\nexport const isRetryableHandoffHttpStatus = (status: number): boolean =>\n status >= 500 || status === 408 || status === 429;\n\n/**\n * Map a non-2xx status to the transport error the poll loop understands.\n * `detail` is the server's actionable message when it sent one.\n */\nexport const handoffHttpError = ({\n handoffId,\n status,\n detail,\n}: {\n handoffId: string;\n status: number;\n detail: string | null;\n}): Error => {\n if (status === 404) return new HandoffGoneError(handoffId);\n const message: string = detail ?? `The handoff request failed (${status}).`;\n return isRetryableHandoffHttpStatus(status) ? new Error(message) : new HandoffPermanentError(message);\n};\n\nexport const describeHandoffTransportError = (error: unknown): string => {\n if (error instanceof z.ZodError) {\n return 'The server sent an unexpected response. Refresh and try again.';\n }\n if (error instanceof Error && error.message) return error.message;\n return 'Lost track of the handoff. Try again.';\n};\n\n/** `?handoff=<id>` — the query parameter mobile-web and QR links carry. */\nexport const HANDOFF_URL_PARAM = 'handoff';\n\n/**\n * Read a handoff id from a URL. Ids are server-issued, URL-safe tokens; anything\n * else is rejected so a tampered link never reaches the claim path.\n */\nexport const parseHandoffIdParam = (value: string | null | undefined): string | null => {\n if (!value) {\n return null;\n }\n return /^[A-Za-z0-9_-]{8,128}$/.test(value) ? value : null;\n};\n\n/** Athena Mobile's custom URL scheme (`mobile-app-react-native/app.json` → `scheme`). */\nexport const MOBILE_APP_SCHEME = 'athenavisionapplication';\n\n/**\n * Custom-scheme launch for a handoff — what a phone's browser opens when the\n * Universal/App Link landed on mobile web instead of in the app. Mirrors\n * agora's `_mobile_scheme_url`; carries only the opaque handoff id.\n */\nexport const buildMobileHandoffSchemeUrl = ({ handoffId }: { handoffId: string }): string =>\n `${MOBILE_APP_SCHEME}://chat?${HANDOFF_URL_PARAM}=${encodeURIComponent(handoffId)}`;\n"],"names":["z.enum","z.object","z.string","z.array","z.boolean","z.literal","z.number","z.discriminatedUnion"],"mappings":";;AAYO,MAAM,uBAAuB,CAAC,UAAU,UAAU,QAAQ,cAAc,OAAO;AAG/E,MAAM,8BAA8B,CAAC,QAAQ,cAAc,OAAO;AAI9B,CAAC,UAAU,GAAG,2BAA2B;AAK7E,MAAM,2BAA2BA,MAAO,CAAC,cAAc,WAAW,WAAW,CAAC;AAG9E,MAAM,qBAAqBA,MAAO,CAAC,UAAU,YAAY,WAAW,gBAAgB,QAAQ,CAAC;AAG7F,MAAM,8BAA8BA,MAAO;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,MAAM,2BAA2BA,MAAO,oBAAoB;AAErD,MAAM,6BAA6BC,OAAS;AAAA,EACjD,aAAa;AAAA,EACb,OAAO;AAAA,EACP,cAAcC,OAAE,EAAS,SAAA,EAAW,SAAA;AAAA,EACpC,OAAOA,OAAE,EAAS,SAAA,EAAW,SAAA;AAAA,EAC7B,iBAAiBA,OAAE,EAAS,SAAA,EAAW,SAAA;AACzC,CAAC;AAG6CD,OAAS;AAAA,EACrD,aAAa;AAAA,EACb,cAAcE,MAAQ,0BAA0B;AAClD,CAAC;AAGM,MAAM,0BAA0BF,OAAS;AAAA,EAC9C,YAAYC,OAAE;AAAA,EACd,WAAWA,OAAE;AAAA,EACb,kBAAkBA,OAAE,EAAS,SAAA;AAAA,EAC7B,QAAQA,OAAE;AAAA,EACV,aAAa;AAAA,EACb,aAAa;AAAA,EACb,OAAO;AAAA,EACP,WAAWA,OAAE;AAAA,EACb,YAAYA,OAAE;AAAA,EACd,YAAYA,OAAE,EAAS,SAAA;AAAA,EACvB,iBAAiBA,OAAE,EAAS,SAAA;AAAA,EAC5B,gBAAgBA,OAAE,EAAS,SAAA;AAAA,EAC3B,gBAAgBE,QAAE,EAAU,SAAA;AAC9B,CAAC;AAGM,MAAM,4BAA4BH,OAAS;AAAA,EAChD,MAAMI,QAAU,QAAQ;AAAA,EACxB,gBAAgBD,QAAE;AAAA,EAClB,gBAAgBA,QAAE;AAAA,EAClB,cAAcE,OAAE;AAAA;AAAA,EAEhB,UAAUJ,OAAE;AAAA;AAAA,EAEZ,YAAYA,OAAE;AAChB,CAAC;AAGM,MAAM,4BAA4BD,OAAS;AAAA,EAChD,MAAMI,QAAU,QAAQ;AAAA,EACxB,MAAML,MAAO,2BAA2B;AAAA;AAAA,EAExC,aAAaE,OAAE;AAAA,EACf,cAAcA,OAAE;AAAA;AAAA,EAEhB,SAASA,OAAE;AAAA,EACX,iBAAiBA,OAAE,EAAS,SAAA;AAC9B,CAAC;AAGM,MAAM,4BAA4BD,OAAS,EAAE,MAAMI,QAAU,QAAQ,EAAA,CAAG;AAExE,MAAM,sBAAsBE,mBAAqB,QAAQ;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGgDN,OAAS;AAAA,EACxD,SAAS;AAAA,EACT,QAAQ;AACV,CAAC;"}
|