@athenaintel/react 0.20.0 → 0.21.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 +74 -0
- package/dist/assets/use-asset-embed.cjs +8 -0
- package/dist/assets/use-asset-embed.cjs.map +1 -1
- package/dist/assets/use-asset-embed.js +8 -0
- package/dist/assets/use-asset-embed.js.map +1 -1
- package/dist/chat/StatewireApprovalCard.cjs +22 -4
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.d.ts +14 -0
- package/dist/chat/StatewireApprovalCard.js +23 -5
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/StatewireClientToolBridge.cjs +2 -1
- package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
- package/dist/chat/StatewireClientToolBridge.js +2 -1
- package/dist/chat/StatewireClientToolBridge.js.map +1 -1
- package/dist/chat/StatewireQueuedMessages.cjs +2 -1
- package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
- package/dist/chat/StatewireQueuedMessages.js +2 -1
- package/dist/chat/StatewireQueuedMessages.js.map +1 -1
- package/dist/chat/athena-citation-links.cjs +17 -1
- package/dist/chat/athena-citation-links.cjs.map +1 -1
- package/dist/chat/athena-citation-links.js +17 -1
- package/dist/chat/athena-citation-links.js.map +1 -1
- package/dist/chat/statewire-client-tools.cjs +2 -1
- package/dist/chat/statewire-client-tools.cjs.map +1 -1
- package/dist/chat/statewire-client-tools.js +2 -1
- package/dist/chat/statewire-client-tools.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.cjs +69 -187
- package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.d.ts +10 -6
- package/dist/chat/super-grouping/SuperGroupingCard.js +73 -191
- package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
- package/dist/chat/super-grouping/ToolTimeline.cjs +131 -0
- package/dist/chat/super-grouping/ToolTimeline.cjs.map +1 -0
- package/dist/chat/super-grouping/ToolTimeline.d.ts +16 -0
- package/dist/chat/super-grouping/ToolTimeline.js +131 -0
- package/dist/chat/super-grouping/ToolTimeline.js.map +1 -0
- package/dist/chat/super-grouping/activity-timeline.cjs +14 -8
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.d.ts +3 -0
- package/dist/chat/super-grouping/activity-timeline.js +14 -8
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/super-grouping/timeline-step.cjs +65 -0
- package/dist/chat/super-grouping/timeline-step.cjs.map +1 -0
- package/dist/chat/super-grouping/timeline-step.d.ts +17 -0
- package/dist/chat/super-grouping/timeline-step.js +65 -0
- package/dist/chat/super-grouping/timeline-step.js.map +1 -0
- package/dist/chat/tiptap-text.cjs +8 -8
- package/dist/chat/tiptap-text.cjs.map +1 -1
- package/dist/chat/use-athena-run-live.cjs +2 -1
- package/dist/chat/use-athena-run-live.cjs.map +1 -1
- package/dist/chat/use-athena-run-live.js +2 -1
- package/dist/chat/use-athena-run-live.js.map +1 -1
- package/dist/chat/use-statewire-approval-request.cjs +7 -2
- package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
- package/dist/chat/use-statewire-approval-request.d.ts +12 -2
- package/dist/chat/use-statewire-approval-request.js +8 -3
- package/dist/chat/use-statewire-approval-request.js.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.cjs +24 -65
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.js +24 -65
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/chat-ui/index.cjs +2 -1
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.js +2 -1
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.cjs +49 -4
- package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.d.ts +11 -0
- package/dist/chat-ui/mentions/mention-chip.js +50 -5
- package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
- package/dist/chat-ui/mentions/mention-extension.cjs +18 -6
- package/dist/chat-ui/mentions/mention-extension.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-extension.d.ts +2 -0
- package/dist/chat-ui/mentions/mention-extension.js +18 -6
- package/dist/chat-ui/mentions/mention-extension.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.cjs +1 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.js +1 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-list.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/menu-item.cjs +9 -6
- package/dist/chat-ui/mentions/suggestions/components/menu-item.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/menu-item.d.ts +2 -2
- package/dist/chat-ui/mentions/suggestions/components/menu-item.js +10 -7
- package/dist/chat-ui/mentions/suggestions/components/menu-item.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/menu-list.cjs +2 -2
- package/dist/chat-ui/mentions/suggestions/components/menu-list.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/menu-list.d.ts +2 -2
- package/dist/chat-ui/mentions/suggestions/components/menu-list.js +2 -2
- package/dist/chat-ui/mentions/suggestions/components/menu-list.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +2 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +3 -2
- package/dist/chat-ui/mentions/suggestions/data/api-client.js +2 -1
- package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
- package/dist/chat-ui/messages/message-components.cjs +6 -29
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.d.ts +1 -13
- package/dist/chat-ui/messages/message-components.js +8 -31
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/chat-ui/messages/runtime-message.cjs +3 -2
- package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
- package/dist/chat-ui/messages/runtime-message.js +3 -2
- package/dist/chat-ui/messages/runtime-message.js.map +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.cjs +23 -8
- package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.d.ts +14 -4
- package/dist/chat-ui/messages/statewire-continue-run.js +23 -8
- package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.cjs +2 -1
- package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.js +2 -1
- package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
- package/dist/chat-ui/rich-text/chat-message-body.cjs +76 -0
- package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -0
- package/dist/chat-ui/rich-text/chat-message-body.d.ts +5 -0
- package/dist/chat-ui/rich-text/chat-message-body.js +76 -0
- package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -0
- package/dist/chat-ui/rich-text/chat-node-views.cjs +51 -0
- package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -0
- package/dist/chat-ui/rich-text/chat-node-views.d.ts +13 -0
- package/dist/chat-ui/rich-text/chat-node-views.js +51 -0
- package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -0
- package/dist/chat-ui/rich-text/chat-text-schema.cjs +70 -0
- package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -0
- package/dist/chat-ui/rich-text/chat-text-schema.d.ts +7 -0
- package/dist/chat-ui/rich-text/chat-text-schema.js +70 -0
- package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -0
- package/dist/chat-ui/rich-text/dom-output-spec.cjs +83 -0
- package/dist/chat-ui/rich-text/dom-output-spec.cjs.map +1 -0
- package/dist/chat-ui/rich-text/dom-output-spec.d.ts +11 -0
- package/dist/chat-ui/rich-text/dom-output-spec.js +83 -0
- package/dist/chat-ui/rich-text/dom-output-spec.js.map +1 -0
- package/dist/chat-ui/statewire.cjs +2 -1
- package/dist/chat-ui/statewire.cjs.map +1 -1
- package/dist/chat-ui/statewire.js +2 -1
- package/dist/chat-ui/statewire.js.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.js +2 -1
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +32 -0
- package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +1 -0
- package/dist/node_modules/qrcode-generator/dist/qrcode.js +31 -0
- package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +1 -0
- package/dist/node_modules/zod/v4/classic/errors.cjs +44 -0
- package/dist/node_modules/zod/v4/classic/errors.cjs.map +1 -0
- package/dist/node_modules/zod/v4/classic/errors.js +44 -0
- package/dist/node_modules/zod/v4/classic/errors.js.map +1 -0
- package/dist/node_modules/zod/v4/classic/iso.cjs +43 -0
- package/dist/node_modules/zod/v4/classic/iso.cjs.map +1 -0
- package/dist/node_modules/zod/v4/classic/iso.js +43 -0
- package/dist/node_modules/zod/v4/classic/iso.js.map +1 -0
- package/dist/node_modules/zod/v4/classic/parse.cjs +29 -0
- package/dist/node_modules/zod/v4/classic/parse.cjs.map +1 -0
- package/dist/node_modules/zod/v4/classic/parse.js +29 -0
- package/dist/node_modules/zod/v4/classic/parse.js.map +1 -0
- package/dist/node_modules/zod/v4/classic/schemas.cjs +871 -0
- package/dist/node_modules/zod/v4/classic/schemas.cjs.map +1 -0
- package/dist/node_modules/zod/v4/classic/schemas.js +871 -0
- package/dist/node_modules/zod/v4/classic/schemas.js.map +1 -0
- package/dist/node_modules/zod/v4/core/api.cjs +570 -0
- package/dist/node_modules/zod/v4/core/api.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/api.js +570 -0
- package/dist/node_modules/zod/v4/core/api.js.map +1 -0
- package/dist/node_modules/zod/v4/core/checks.cjs +411 -0
- package/dist/node_modules/zod/v4/core/checks.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/checks.js +411 -0
- package/dist/node_modules/zod/v4/core/checks.js.map +1 -0
- package/dist/node_modules/zod/v4/core/core.cjs +77 -0
- package/dist/node_modules/zod/v4/core/core.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/core.js +77 -0
- package/dist/node_modules/zod/v4/core/core.js.map +1 -0
- package/dist/node_modules/zod/v4/core/doc.cjs +38 -0
- package/dist/node_modules/zod/v4/core/doc.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/doc.js +38 -0
- package/dist/node_modules/zod/v4/core/doc.js.map +1 -0
- package/dist/node_modules/zod/v4/core/errors.cjs +76 -0
- package/dist/node_modules/zod/v4/core/errors.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/errors.js +76 -0
- package/dist/node_modules/zod/v4/core/errors.js.map +1 -0
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +312 -0
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/json-schema-processors.js +312 -0
- package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +1 -0
- package/dist/node_modules/zod/v4/core/parse.cjs +96 -0
- package/dist/node_modules/zod/v4/core/parse.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/parse.js +96 -0
- package/dist/node_modules/zod/v4/core/parse.js.map +1 -0
- package/dist/node_modules/zod/v4/core/regexes.cjs +86 -0
- package/dist/node_modules/zod/v4/core/regexes.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/regexes.js +86 -0
- package/dist/node_modules/zod/v4/core/regexes.js.map +1 -0
- package/dist/node_modules/zod/v4/core/registries.cjs +52 -0
- package/dist/node_modules/zod/v4/core/registries.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/registries.js +52 -0
- package/dist/node_modules/zod/v4/core/registries.js.map +1 -0
- package/dist/node_modules/zod/v4/core/schemas.cjs +1412 -0
- package/dist/node_modules/zod/v4/core/schemas.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/schemas.js +1413 -0
- package/dist/node_modules/zod/v4/core/schemas.js.map +1 -0
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs +370 -0
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/to-json-schema.js +370 -0
- package/dist/node_modules/zod/v4/core/to-json-schema.js.map +1 -0
- package/dist/node_modules/zod/v4/core/util.cjs +444 -0
- package/dist/node_modules/zod/v4/core/util.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/util.js +444 -0
- package/dist/node_modules/zod/v4/core/util.js.map +1 -0
- package/dist/node_modules/zod/v4/core/versions.cjs +9 -0
- package/dist/node_modules/zod/v4/core/versions.cjs.map +1 -0
- package/dist/node_modules/zod/v4/core/versions.js +9 -0
- package/dist/node_modules/zod/v4/core/versions.js.map +1 -0
- package/dist/packages/agent-runtime/src/client-tool-bridge.cjs +7 -1
- package/dist/packages/agent-runtime/src/client-tool-bridge.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/client-tool-bridge.js +7 -1
- package/dist/packages/agent-runtime/src/client-tool-bridge.js.map +1 -1
- package/dist/packages/agent-runtime/src/conversation-list.cjs +4 -0
- package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/conversation-list.js +4 -0
- package/dist/packages/agent-runtime/src/conversation-list.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/generated/runconfig-contract.cjs +2 -0
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +2 -0
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.cjs +31 -7
- package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.js +31 -7
- package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/packages/agent-runtime/src/messages.cjs +35 -5
- package/dist/packages/agent-runtime/src/messages.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/messages.js +35 -5
- package/dist/packages/agent-runtime/src/messages.js.map +1 -1
- package/dist/packages/agent-runtime/src/protocol.cjs +4 -6
- package/dist/packages/agent-runtime/src/protocol.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/protocol.js +4 -6
- package/dist/packages/agent-runtime/src/protocol.js.map +1 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +296 -14
- 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 +293 -11
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
- package/dist/packages/agent-runtime/src/screen-point.cjs +68 -0
- package/dist/packages/agent-runtime/src/screen-point.cjs.map +1 -0
- package/dist/packages/agent-runtime/src/screen-point.js +68 -0
- package/dist/packages/agent-runtime/src/screen-point.js.map +1 -0
- package/dist/packages/agent-runtime/src/statewire-transport.cjs +6 -1
- package/dist/packages/agent-runtime/src/statewire-transport.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/statewire-transport.js +6 -1
- package/dist/packages/agent-runtime/src/statewire-transport.js.map +1 -1
- package/dist/packages/agent-runtime/src/thread-handoff.cjs +85 -0
- package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +1 -0
- package/dist/packages/agent-runtime/src/thread-handoff.js +85 -0
- package/dist/packages/agent-runtime/src/thread-handoff.js.map +1 -0
- package/dist/packages/agent-runtime/src/thread-snapshot-cache.cjs +1 -1
- package/dist/packages/agent-runtime/src/thread-snapshot-cache.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/thread-snapshot-cache.js +1 -1
- package/dist/packages/agent-runtime/src/thread-snapshot-cache.js.map +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.cjs +0 -8
- package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.js +0 -8
- package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +14 -0
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -0
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js +14 -0
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -0
- package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs +21 -5
- package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js +21 -5
- package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-runtime.cjs +13 -3
- package/dist/packages/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-runtime.js +14 -4
- package/dist/packages/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs +35 -15
- 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 +35 -15
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/packages/agent-runtime/src/voice-presentation.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/voice-presentation.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 +2 -1
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.js +2 -1
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/runtime/statewire-lifecycle.cjs +2 -1
- package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
- package/dist/runtime/statewire-lifecycle.js +2 -1
- package/dist/runtime/statewire-lifecycle.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +2 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +2 -1
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/threads/conversation-list-fetcher.cjs +4 -0
- package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
- package/dist/threads/conversation-list-fetcher.js +4 -0
- package/dist/threads/conversation-list-fetcher.js.map +1 -1
- package/dist/threads/statewire-thread-list.cjs +2 -1
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.js +2 -1
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/threads/use-rest-conversation-history.cjs +2 -1
- package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
- package/dist/threads/use-rest-conversation-history.js +2 -1
- package/dist/threads/use-rest-conversation-history.js.map +1 -1
- package/dist/threads/use-statewire-conversation-history.cjs +2 -1
- package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
- package/dist/threads/use-statewire-conversation-history.d.ts +1 -0
- package/dist/threads/use-statewire-conversation-history.js +2 -1
- package/dist/threads/use-statewire-conversation-history.js.map +1 -1
- package/dist/tools/index.cjs +2 -1
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/misc-tool-uis.cjs +2 -1
- package/dist/tools/misc-tool-uis.cjs.map +1 -1
- package/dist/tools/misc-tool-uis.js +2 -1
- package/dist/tools/misc-tool-uis.js.map +1 -1
- package/dist/tools/primitives.cjs +2 -1
- package/dist/tools/primitives.cjs.map +1 -1
- package/dist/tools/primitives.js +2 -1
- package/dist/tools/primitives.js.map +1 -1
- package/dist/tools/tool-fallback.cjs +2 -1
- package/dist/tools/tool-fallback.cjs.map +1 -1
- package/dist/tools/tool-fallback.js +2 -1
- package/dist/tools/tool-fallback.js.map +1 -1
- package/dist/tools/voice-update-tool-ui.cjs +2 -1
- package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
- package/dist/tools/voice-update-tool-ui.js +2 -1
- package/dist/tools/voice-update-tool-ui.js.map +1 -1
- package/dist/voice/index.cjs +6 -0
- package/dist/voice/index.cjs.map +1 -0
- package/dist/voice/index.d.ts +2 -0
- package/dist/voice/index.js +6 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/use-athena-voice-session.cjs +315 -0
- package/dist/voice/use-athena-voice-session.cjs.map +1 -0
- package/dist/voice/use-athena-voice-session.d.ts +145 -0
- package/dist/voice/use-athena-voice-session.js +315 -0
- package/dist/voice/use-athena-voice-session.js.map +1 -0
- package/package.json +17 -3
package/README.md
CHANGED
|
@@ -157,6 +157,80 @@ silently ignored selector would let a deployment answer as the wrong agent.
|
|
|
157
157
|
`model` defaults to the deep-agent default model (`DEFAULT_STATEWIRE_MODEL`)
|
|
158
158
|
unless a collab agent supplies one.
|
|
159
159
|
|
|
160
|
+
## Voice Sessions (`@athenaintel/react/voice`)
|
|
161
|
+
|
|
162
|
+
`useAthenaVoiceSession` runs a real-time voice conversation with an Athena
|
|
163
|
+
voice agent (Orpheus, over LiveKit) and streams the transcript into React
|
|
164
|
+
state. It owns the whole browser side — session creation, room connect, mic,
|
|
165
|
+
agent audio, live/interim transcript turns, reconnects, StrictMode-safe
|
|
166
|
+
teardown — so an app is a `start()` button and a transcript list.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
bun add @athenaintel/react livekit-client # livekit-client is an optional peer dep
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { useAthenaVoiceSession } from '@athenaintel/react/voice';
|
|
174
|
+
|
|
175
|
+
const voice = useAthenaVoiceSession({
|
|
176
|
+
session: {
|
|
177
|
+
system_prompt: 'You are a customer at an auto-parts store...',
|
|
178
|
+
provider: 'openai', // "google" | "openai"
|
|
179
|
+
model: 'gpt-realtime-2',
|
|
180
|
+
voice: 'marin',
|
|
181
|
+
enabled_tools: [], // [] = pure conversation
|
|
182
|
+
},
|
|
183
|
+
onTranscript: (turn) => console.log(turn.speaker, turn.text),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// voice.status: 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'ended' | 'error'
|
|
187
|
+
// voice.transcript: AthenaVoiceTranscriptTurn[] voice.sessionId: asset id
|
|
188
|
+
<button onClick={voice.start}>Talk</button>
|
|
189
|
+
<button onClick={voice.stop}>Hang up</button>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Authentication.** Served from a Marathon computer-asset URL, the hook is
|
|
193
|
+
zero-config: it takes the viewer bearer from `useParentBridge()` and creates
|
|
194
|
+
the session **as the person using the app**, so it shows up under their
|
|
195
|
+
Sessions in Athena and transcript/grading calls are authorized as them. This
|
|
196
|
+
works for workspace members and for external SSO viewers alike: the viewer
|
|
197
|
+
credential minted by `/_athena/auth` is authorized for session creation and
|
|
198
|
+
grading the same way it is for `/api/v0/sessions`, scoped to the viewer's own
|
|
199
|
+
workspace. Anywhere else — standalone hosting, custom auth — pass
|
|
200
|
+
`createSession` and create the session from your own server (API key stays server-side):
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
useAthenaVoiceSession({
|
|
204
|
+
createSession: (signal) =>
|
|
205
|
+
fetch('/api/voice/session', { method: 'POST', signal }).then((r) => r.json()),
|
|
206
|
+
// → { server_url, token, room_name, asset_id }
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Off Marathon without `createSession`, `start()` fails with an explicit error;
|
|
211
|
+
it never falls back to a shared key.
|
|
212
|
+
|
|
213
|
+
**After the call.** `voice.sessionId` is a regular Athena session asset:
|
|
214
|
+
|
|
215
|
+
- Transcript: `GET /api/v0/sessions/{asset_id}/download?format=messages`
|
|
216
|
+
- Grading against a rubric: `POST /api/v0/agents/general/invoke` with
|
|
217
|
+
`config.structured_output` (a JSON Schema) → typed `structured_output` in
|
|
218
|
+
the response.
|
|
219
|
+
|
|
220
|
+
Both use the same bearer / API key as session creation. The full recipe
|
|
221
|
+
(persona → roleplay → transcript → rubric score) lives in the computer
|
|
222
|
+
template skill `voice-session-guide.md`. `createAthenaVoiceSession` is also
|
|
223
|
+
exported if you hold the bridge state yourself.
|
|
224
|
+
|
|
225
|
+
## What's new in 0.21.0
|
|
226
|
+
|
|
227
|
+
0.21.0 adds the `@athenaintel/react/voice` entry: `useAthenaVoiceSession`
|
|
228
|
+
(LiveKit lifecycle, mic, agent audio, live transcript turns, reconnect,
|
|
229
|
+
StrictMode-safe) and `createAthenaVoiceSession`. On Marathon it is
|
|
230
|
+
zero-config and attributes the session to the viewer (workspace members and
|
|
231
|
+
external SSO viewers); elsewhere pass `createSession`. See
|
|
232
|
+
[Voice Sessions](#voice-sessions-athenaintelreactvoice). No breaking changes.
|
|
233
|
+
|
|
160
234
|
## What's new in 0.20.0
|
|
161
235
|
|
|
162
236
|
0.20.0 finishes the composer unification and retires the last chat-wire URL
|
|
@@ -121,6 +121,11 @@ function setFiniteNumberParam({ params, key, value }) {
|
|
|
121
121
|
params.set(key, String(value));
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
+
function setNonEmptyStringParam({ params, key, value }) {
|
|
125
|
+
if (typeof value === "string" && value.length > 0) {
|
|
126
|
+
params.set(key, value);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
124
129
|
function appendCitationReferenceParams({
|
|
125
130
|
params,
|
|
126
131
|
reference
|
|
@@ -193,6 +198,9 @@ function appendCitationReferenceParams({
|
|
|
193
198
|
if ("text" in anchor && typeof anchor.text === "string") {
|
|
194
199
|
params.set("text", anchor.text);
|
|
195
200
|
}
|
|
201
|
+
setNonEmptyStringParam({ params, key: "prefix", value: "prefix" in anchor ? anchor.prefix : void 0 });
|
|
202
|
+
setNonEmptyStringParam({ params, key: "suffix", value: "suffix" in anchor ? anchor.suffix : void 0 });
|
|
203
|
+
setNonEmptyStringParam({ params, key: "hash", value: "hash" in anchor ? anchor.hash : void 0 });
|
|
196
204
|
break;
|
|
197
205
|
// Sheet anchors ride the same query contract the embed page parses with
|
|
198
206
|
// the shared `@athenaintel/references` parser.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-asset-embed.cjs","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (!anchor || typeof anchor !== 'object' || !('type' in anchor) || typeof anchor.type !== 'string') {\n return;\n }\n\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n\n const excerpt = reference.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const currentToken = tokenRef.current;\n if (currentToken) {\n headers.Authorization = `Bearer ${currentToken}`;\n } else if (apiKey) {\n headers['X-API-KEY'] = apiKey;\n }\n\n fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n })\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":["deriveAthenaAppUrl","useState","useRef","useEffect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-asset-embed.cjs","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (!anchor || typeof anchor !== 'object' || !('type' in anchor) || typeof anchor.type !== 'string') {\n return;\n }\n\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n\n const excerpt = reference.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const currentToken = tokenRef.current;\n if (currentToken) {\n headers.Authorization = `Bearer ${currentToken}`;\n } else if (apiKey) {\n headers['X-API-KEY'] = apiKey;\n }\n\n fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n })\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":["deriveAthenaAppUrl","useState","useRef","useEffect"],"mappings":"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"}
|
|
@@ -119,6 +119,11 @@ function setFiniteNumberParam({ params, key, value }) {
|
|
|
119
119
|
params.set(key, String(value));
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
function setNonEmptyStringParam({ params, key, value }) {
|
|
123
|
+
if (typeof value === "string" && value.length > 0) {
|
|
124
|
+
params.set(key, value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
122
127
|
function appendCitationReferenceParams({
|
|
123
128
|
params,
|
|
124
129
|
reference
|
|
@@ -191,6 +196,9 @@ function appendCitationReferenceParams({
|
|
|
191
196
|
if ("text" in anchor && typeof anchor.text === "string") {
|
|
192
197
|
params.set("text", anchor.text);
|
|
193
198
|
}
|
|
199
|
+
setNonEmptyStringParam({ params, key: "prefix", value: "prefix" in anchor ? anchor.prefix : void 0 });
|
|
200
|
+
setNonEmptyStringParam({ params, key: "suffix", value: "suffix" in anchor ? anchor.suffix : void 0 });
|
|
201
|
+
setNonEmptyStringParam({ params, key: "hash", value: "hash" in anchor ? anchor.hash : void 0 });
|
|
194
202
|
break;
|
|
195
203
|
// Sheet anchors ride the same query contract the embed page parses with
|
|
196
204
|
// the shared `@athenaintel/references` parser.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (!anchor || typeof anchor !== 'object' || !('type' in anchor) || typeof anchor.type !== 'string') {\n return;\n }\n\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n\n const excerpt = reference.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const currentToken = tokenRef.current;\n if (currentToken) {\n headers.Authorization = `Bearer ${currentToken}`;\n } else if (apiKey) {\n headers['X-API-KEY'] = apiKey;\n }\n\n fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n })\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (!anchor || typeof anchor !== 'object' || !('type' in anchor) || typeof anchor.type !== 'string') {\n return;\n }\n\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n\n const excerpt = reference.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const currentToken = tokenRef.current;\n if (currentToken) {\n headers.Authorization = `Bearer ${currentToken}`;\n } else if (apiKey) {\n headers['X-API-KEY'] = apiKey;\n }\n\n fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n })\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -14,10 +14,11 @@ require("../packages/agent-runtime/src/statewire-state.cjs");
|
|
|
14
14
|
const threadExtras = require("../packages/agent-runtime/src/thread-extras.cjs");
|
|
15
15
|
require("@assistant-ui/react-statewire");
|
|
16
16
|
require("@assistant-ui/store");
|
|
17
|
-
require("
|
|
17
|
+
require("../packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
|
|
18
18
|
require("../packages/agent-runtime/src/statewire-attach-observer.cjs");
|
|
19
19
|
require("../packages/agent-runtime/src/thread-status.cjs");
|
|
20
20
|
require("@assistant-ui/core");
|
|
21
|
+
require("../packages/agent-runtime/src/thread-handoff.cjs");
|
|
21
22
|
const lucideReact = require("lucide-react");
|
|
22
23
|
const button = require("../ui/button.cjs");
|
|
23
24
|
const useStatewireApprovalRequest = require("./use-statewire-approval-request.cjs");
|
|
@@ -35,10 +36,12 @@ const StatewireApprovalCardInner = ({
|
|
|
35
36
|
isHandledElsewhere,
|
|
36
37
|
renderDetails,
|
|
37
38
|
readOnly = false,
|
|
38
|
-
readOnlyNotice = "Waiting for someone with edit access to respond."
|
|
39
|
+
readOnlyNotice = "Waiting for someone with edit access to respond.",
|
|
40
|
+
nonHitlNotice = "This request needs an answer this surface cannot collect.",
|
|
41
|
+
onResumeRejected
|
|
39
42
|
}) => {
|
|
40
43
|
var _a, _b, _c;
|
|
41
|
-
const { request, requestId, hitl, actionRequests, abandonedInterrupt, pending, resume } = useStatewireApprovalRequest.useStatewireApprovalRequest({ interruptPredicate, pendingResumeGraceMs });
|
|
44
|
+
const { request, requestId, hitl, actionRequests, abandonedInterrupt, pending, resume } = useStatewireApprovalRequest.useStatewireApprovalRequest({ interruptPredicate, pendingResumeGraceMs, onResumeRejected });
|
|
42
45
|
const [dismissedId, setDismissedId] = react$1.useState();
|
|
43
46
|
const [detailsOverride, setDetailsOverride] = react$1.useState();
|
|
44
47
|
const prevRequestIdRef = react$1.useRef(requestId);
|
|
@@ -149,7 +152,22 @@ const StatewireApprovalCardInner = ({
|
|
|
149
152
|
]
|
|
150
153
|
}
|
|
151
154
|
)
|
|
152
|
-
] }) : /* @__PURE__ */ jsxRuntime.
|
|
155
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
156
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-amber-800 text-sm", "data-testid": "statewire-approval-non-hitl", children: nonHitlNotice }),
|
|
157
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
158
|
+
button.Button,
|
|
159
|
+
{
|
|
160
|
+
size: "sm",
|
|
161
|
+
variant: "outline",
|
|
162
|
+
disabled: pending,
|
|
163
|
+
onClick: () => resume(interrupts.interruptResumeDecline()),
|
|
164
|
+
children: [
|
|
165
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, {}),
|
|
166
|
+
"Decline"
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
] }) })
|
|
153
171
|
]
|
|
154
172
|
}
|
|
155
173
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatewireApprovalCard.cjs","sources":["../../src/chat/StatewireApprovalCard.tsx"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport {\n hasStatewireThreadExtras,\n type HitlApprovalPayload,\n hitlResumeApprove,\n hitlResumeContinue,\n hitlResumeReject,\n type PendingActionRequest,\n readInterruptMessage,\n} from '@athenaintel/agent-runtime';\nimport { CheckIcon, ChevronDownIcon, TriangleAlertIcon, XIcon } from 'lucide-react';\nimport { type FC, type ReactNode, useRef, useState } from 'react';\nimport { cn } from '../lib/utils';\nimport { Button } from '../ui/button';\nimport {\n type StatewireInterruptPredicate,\n useStatewireApprovalRequest,\n} from './use-statewire-approval-request';\n\n/**\n * Human-in-the-loop approval card for the statewire transport — the one\n * approval UI every Athena chat surface mounts (the SDK's `AthenaChat`, the\n * Chrome side panel, the Office task pane, Olympus).\n *\n * When the deep-agent runtime hits an approval-gated tool call, the backend\n * parks the run on an `interrupt` input request. This card reads that request\n * through `useStatewireApprovalRequest` and answers it with\n * `{ type: 'run/input', requestId, response: { type: 'resume', value } }` —\n * the exact command the backend's resume path consumes. Without an answer the\n * run stays parked forever, so this card must be mounted on every statewire\n * chat surface. On legacy (non-statewire) threads it renders nothing.\n *\n * Host-specific review UIs (subagent cards, email summaries) slot in through\n * `renderDetails`; requests another surface answers (typed interrupt\n * families, per-lane subagent approvals) stand down through\n * `isHandledElsewhere`; viewers without edit rights see `readOnlyNotice`.\n */\n\n/** The pending request a host slot renders or judges. */\nexport interface StatewireApprovalRequestContext {\n request: StatewireThread.CustomInputRequest;\n /** The payload narrowed to the Athena HITL approval shape, or `null` for a generic park. */\n hitl: HitlApprovalPayload | null;\n /** Every gated call the approval covers, in decision order (empty for non-HITL parks). */\n actionRequests: PendingActionRequest[];\n}\n\nexport interface StatewireApprovalCardProps {\n /** Surface-styling overrides merged onto the default classes. */\n classNames?: {\n /** The approval card container. */\n root?: string;\n /** The \"approval was cancelled\" notice container. */\n abandoned?: string;\n /** The disclosed details block. */\n details?: string;\n /** The decision row. */\n actions?: string;\n };\n /** Copy shown when an interrupt payload carries no message. */\n fallbackMessage?: string;\n /**\n * Which pending interrupt this card answers. Defaults to\n * `isApprovalCardInterrupt` (client-tool parks excluded — keep it that way\n * unless the surface handles those itself).\n */\n interruptPredicate?: StatewireInterruptPredicate;\n /**\n * How long a clicked decision keeps the buttons locked when no resume run\n * ever starts (a `run/input` the host rejected before any run began).\n * Defaults to `HITL_PENDING_RESUME_GRACE_MS`.\n */\n pendingResumeGraceMs?: number;\n /**\n * Return true when another surface answers this request (a typed Athena\n * interrupt family, per-lane subagent approval cards). The card renders\n * nothing for it — two surfaces must never answer the same input request.\n */\n isHandledElsewhere?: (context: StatewireApprovalRequestContext) => boolean;\n /**\n * Replace the default argument disclosure (one JSON block per gated call)\n * with a host-specific review UI. Rendered inside the collapsible details\n * region; the decision buttons stay with the card.\n */\n renderDetails?: (context: StatewireApprovalRequestContext) => ReactNode;\n /**\n * The viewer may see the request (it explains why the run is parked) but\n * cannot decide it — answering steers the shared run. Renders\n * `readOnlyNotice` in place of the buttons.\n */\n readOnly?: boolean;\n readOnlyNotice?: ReactNode;\n}\n\nexport const StatewireApprovalCard: FC<StatewireApprovalCardProps> = (props) => {\n const extras = useAuiState((s) => s.thread.extras);\n if (!hasStatewireThreadExtras(extras)) return null;\n return <StatewireApprovalCardInner {...props} />;\n};\n\nconst StatewireApprovalCardInner: FC<StatewireApprovalCardProps> = ({\n classNames,\n fallbackMessage,\n interruptPredicate,\n pendingResumeGraceMs,\n isHandledElsewhere,\n renderDetails,\n readOnly = false,\n readOnlyNotice = 'Waiting for someone with edit access to respond.',\n}) => {\n const { request, requestId, hitl, actionRequests, abandonedInterrupt, pending, resume } =\n useStatewireApprovalRequest({ interruptPredicate, pendingResumeGraceMs });\n const [dismissedId, setDismissedId] = useState<string>();\n // The card shares the chat column with the thread. A lone gated call is\n // small enough to show inline, but a fan-out of a dozen calls would swallow\n // the conversation, so a batch starts collapsed until someone opens it.\n const [detailsOverride, setDetailsOverride] = useState<boolean>();\n\n // A fresh approval gets the default for ITS batch size, not the disclosure\n // the previous one was left in. Reset during render via React's \"previous\n // value\" pattern.\n const prevRequestIdRef = useRef(requestId);\n if (prevRequestIdRef.current !== requestId) {\n prevRequestIdRef.current = requestId;\n if (detailsOverride !== undefined) setDetailsOverride(undefined);\n }\n\n if (!request) {\n if (!abandonedInterrupt || abandonedInterrupt.id === dismissedId) return null;\n return (\n <div\n className={cn(\n 'aui-approval-abandoned flex w-full items-center justify-between gap-3 rounded-xl border border-amber-300 bg-amber-50 px-3 py-2 text-amber-800 text-xs',\n classNames?.abandoned,\n )}\n >\n <p>Approval was cancelled — the run moved on.</p>\n <button\n type=\"button\"\n aria-label=\"Dismiss cancelled approval\"\n onClick={() => setDismissedId(abandonedInterrupt.id)}\n className=\"shrink-0 rounded-md p-1 hover:bg-amber-100\"\n >\n <XIcon className=\"size-3.5\" />\n </button>\n </div>\n );\n }\n\n const context: StatewireApprovalRequestContext = { request, hitl, actionRequests };\n if (isHandledElsewhere?.(context)) return null;\n\n const message = readInterruptMessage(request.payload, fallbackMessage);\n const toolName = hitl?.context?.tool_name ?? hitl?.context?.tool_id;\n const pendingCount = hitl?.context?.pending_action_count ?? 1;\n const hasDetails = hitl !== null && actionRequests.length > 0;\n const showDetails = hasDetails && (detailsOverride ?? actionRequests.length <= 1);\n\n return (\n <section\n aria-label={hitl ? 'Approval required' : 'Input required'}\n className={cn(\n 'aui-approval-card w-full rounded-2xl border border-amber-300 bg-amber-50 px-4 py-3 text-amber-900',\n classNames?.root,\n )}\n >\n <div className=\"flex items-start gap-3\">\n <TriangleAlertIcon className=\"mt-0.5 size-4 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"flex items-center gap-2 font-semibold text-sm\">\n <span>{hitl ? 'Approval required' : 'Waiting for input'}</span>\n {hitl && pendingCount > 1 ? (\n <span className=\"shrink-0 font-normal text-amber-800 text-xs\">\n {pendingCount} tool calls\n </span>\n ) : null}\n </p>\n <p className=\"pt-1 text-amber-800 text-sm leading-snug\">\n {hitl && pendingCount === 1 && toolName ? (\n <>\n <span className=\"font-mono\">{toolName}</span> requires approval before it runs.\n </>\n ) : (\n message\n )}\n </p>\n {hasDetails ? (\n <button\n type=\"button\"\n onClick={() => setDetailsOverride(!showDetails)}\n aria-expanded={showDetails}\n className=\"mt-2 flex items-center gap-1 rounded px-1 py-0.5 text-amber-800 text-xs transition-colors hover:text-amber-950\"\n >\n <ChevronDownIcon\n className={cn('size-3 transition-transform', showDetails && 'rotate-180')}\n />\n {showDetails ? 'Hide details' : 'Review details'}\n </button>\n ) : null}\n {/* EVERY pending gated call renders, in order — one decision\n authorizes the whole batch, so nothing may hide behind a host's\n richer cards. All read-only: this runtime resumes through\n `hitl_response_to_decisions`, which has no `tool_args` channel —\n an editable composer would let someone rewrite an email and then\n watch the original go out. */}\n {showDetails ? (\n <div className={cn('mt-2 flex flex-col gap-2', classNames?.details)}>\n {renderDetails ? (\n renderDetails(context)\n ) : (\n <DefaultApprovalDetails actionRequests={actionRequests} />\n )}\n </div>\n ) : null}\n </div>\n </div>\n <div className={cn('flex flex-wrap items-center gap-2 pt-3', classNames?.actions)}>\n {readOnly ? (\n <p className=\"text-amber-800 text-sm\" data-testid=\"statewire-approval-read-only\">\n {readOnlyNotice}\n </p>\n ) : hitl ? (\n <>\n <Button\n size=\"sm\"\n disabled={pending}\n onClick={() => resume(hitlResumeApprove())}\n className=\"bg-green-600 text-white hover:bg-green-700\"\n >\n <CheckIcon />\n Approve\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={pending}\n onClick={() => resume(hitlResumeReject())}\n >\n <XIcon />\n Reject\n </Button>\n </>\n ) : (\n <Button size=\"sm\" disabled={pending} onClick={() => resume(hitlResumeContinue())}>\n Continue\n </Button>\n )}\n </div>\n </section>\n );\n};\n\n/** One block per gated call: its name (when the batch has several) and its arguments as JSON. */\nconst DefaultApprovalDetails: FC<{ actionRequests: PendingActionRequest[] }> = ({\n actionRequests,\n}) => (\n <>\n {actionRequests.map((action, index) => (\n <div\n key={`${action.name}-${index}`}\n className=\"rounded-lg bg-amber-100 p-2 text-amber-900 text-xs leading-relaxed\"\n >\n {actionRequests.length > 1 ? (\n <p className=\"font-mono font-semibold\">{action.name}</p>\n ) : null}\n {Object.keys(action.args).length > 0 ? (\n <pre className=\"mt-1 max-h-32 overflow-auto whitespace-pre-wrap break-words rounded bg-white/60 p-1.5 font-mono text-[11px]\">\n {JSON.stringify(action.args, null, 2)}\n </pre>\n ) : (\n <p className=\"text-amber-800\">No arguments.</p>\n )}\n </div>\n ))}\n </>\n);\n"],"names":["useAuiState","hasStatewireThreadExtras","jsx","useStatewireApprovalRequest","useState","useRef","jsxs","cn","XIcon","readInterruptMessage","TriangleAlertIcon","Fragment","ChevronDownIcon","Button","hitlResumeApprove","CheckIcon","hitlResumeReject","hitlResumeContinue"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StatewireApprovalCard.cjs","sources":["../../src/chat/StatewireApprovalCard.tsx"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport {\n hasStatewireThreadExtras,\n type HitlApprovalPayload,\n hitlResumeApprove,\n hitlResumeReject,\n interruptResumeDecline,\n type PendingActionRequest,\n readInterruptMessage,\n} from '@athenaintel/agent-runtime';\nimport { CheckIcon, ChevronDownIcon, TriangleAlertIcon, XIcon } from 'lucide-react';\nimport { type FC, type ReactNode, useRef, useState } from 'react';\nimport { cn } from '../lib/utils';\nimport { Button } from '../ui/button';\nimport {\n type StatewireInterruptPredicate,\n useStatewireApprovalRequest,\n} from './use-statewire-approval-request';\n\n/**\n * Human-in-the-loop approval card for the statewire transport — the one\n * approval UI every Athena chat surface mounts (the SDK's `AthenaChat`, the\n * Chrome side panel, the Office task pane, Olympus).\n *\n * When the deep-agent runtime hits an approval-gated tool call, the backend\n * parks the run on an `interrupt` input request. This card reads that request\n * through `useStatewireApprovalRequest` and answers it with\n * `{ type: 'run/input', requestId, response: { type: 'resume', value } }` —\n * the exact command the backend's resume path consumes. Without an answer the\n * run stays parked forever, so this card must be mounted on every statewire\n * chat surface. On legacy (non-statewire) threads it renders nothing.\n *\n * Host-specific review UIs (subagent cards, email summaries) slot in through\n * `renderDetails`; requests another surface answers (typed interrupt\n * families, per-lane subagent approvals) stand down through\n * `isHandledElsewhere`; viewers without edit rights see `readOnlyNotice`.\n */\n\n/** The pending request a host slot renders or judges. */\nexport interface StatewireApprovalRequestContext {\n request: StatewireThread.CustomInputRequest;\n /** The payload narrowed to the Athena HITL approval shape, or `null` for a generic park. */\n hitl: HitlApprovalPayload | null;\n /** Every gated call the approval covers, in decision order (empty for non-HITL parks). */\n actionRequests: PendingActionRequest[];\n}\n\nexport interface StatewireApprovalCardProps {\n /** Surface-styling overrides merged onto the default classes. */\n classNames?: {\n /** The approval card container. */\n root?: string;\n /** The \"approval was cancelled\" notice container. */\n abandoned?: string;\n /** The disclosed details block. */\n details?: string;\n /** The decision row. */\n actions?: string;\n };\n /** Copy shown when an interrupt payload carries no message. */\n fallbackMessage?: string;\n /**\n * Which pending interrupt this card answers. Defaults to\n * `isApprovalCardInterrupt` (client-tool parks excluded — keep it that way\n * unless the surface handles those itself).\n */\n interruptPredicate?: StatewireInterruptPredicate;\n /**\n * How long a clicked decision keeps the buttons locked when no resume run\n * ever starts (a `run/input` the host rejected before any run began).\n * Defaults to `HITL_PENDING_RESUME_GRACE_MS`.\n */\n pendingResumeGraceMs?: number;\n /**\n * Return true when another surface answers this request (a typed Athena\n * interrupt family, per-lane subagent approval cards). The card renders\n * nothing for it — two surfaces must never answer the same input request.\n */\n isHandledElsewhere?: (context: StatewireApprovalRequestContext) => boolean;\n /**\n * Replace the default argument disclosure (one JSON block per gated call)\n * with a host-specific review UI. Rendered inside the collapsible details\n * region; the decision buttons stay with the card.\n */\n renderDetails?: (context: StatewireApprovalRequestContext) => ReactNode;\n /**\n * The viewer may see the request (it explains why the run is parked) but\n * cannot decide it — answering steers the shared run. Renders\n * `readOnlyNotice` in place of the buttons.\n */\n readOnly?: boolean;\n readOnlyNotice?: ReactNode;\n /**\n * Copy for a park that is not a HITL approval — a typed Athena interrupt\n * this surface has no card for. The only honest action is to decline it\n * (`{ action: 'reject' }`, which every typed raiser treats as a clean user\n * decline); a generic \"Continue\" would be rejected by the raiser's resume\n * schema and surface as a tool error.\n */\n nonHitlNotice?: ReactNode;\n /**\n * The host refused a decision (or the transport lost it). The optimistic\n * fold has rolled back and the request is on screen again with its actions\n * re-enabled; this is where a host tells the user why (a toast, a log line).\n */\n onResumeRejected?: (error: unknown, requestId: string) => void;\n}\n\nexport const StatewireApprovalCard: FC<StatewireApprovalCardProps> = (props) => {\n const extras = useAuiState((s) => s.thread.extras);\n if (!hasStatewireThreadExtras(extras)) return null;\n return <StatewireApprovalCardInner {...props} />;\n};\n\nconst StatewireApprovalCardInner: FC<StatewireApprovalCardProps> = ({\n classNames,\n fallbackMessage,\n interruptPredicate,\n pendingResumeGraceMs,\n isHandledElsewhere,\n renderDetails,\n readOnly = false,\n readOnlyNotice = 'Waiting for someone with edit access to respond.',\n nonHitlNotice = 'This request needs an answer this surface cannot collect.',\n onResumeRejected,\n}) => {\n const { request, requestId, hitl, actionRequests, abandonedInterrupt, pending, resume } =\n useStatewireApprovalRequest({ interruptPredicate, pendingResumeGraceMs, onResumeRejected });\n const [dismissedId, setDismissedId] = useState<string>();\n // The card shares the chat column with the thread. A lone gated call is\n // small enough to show inline, but a fan-out of a dozen calls would swallow\n // the conversation, so a batch starts collapsed until someone opens it.\n const [detailsOverride, setDetailsOverride] = useState<boolean>();\n\n // A fresh approval gets the default for ITS batch size, not the disclosure\n // the previous one was left in. Reset during render via React's \"previous\n // value\" pattern.\n const prevRequestIdRef = useRef(requestId);\n if (prevRequestIdRef.current !== requestId) {\n prevRequestIdRef.current = requestId;\n if (detailsOverride !== undefined) setDetailsOverride(undefined);\n }\n\n if (!request) {\n if (!abandonedInterrupt || abandonedInterrupt.id === dismissedId) return null;\n return (\n <div\n className={cn(\n 'aui-approval-abandoned flex w-full items-center justify-between gap-3 rounded-xl border border-amber-300 bg-amber-50 px-3 py-2 text-amber-800 text-xs',\n classNames?.abandoned,\n )}\n >\n <p>Approval was cancelled — the run moved on.</p>\n <button\n type=\"button\"\n aria-label=\"Dismiss cancelled approval\"\n onClick={() => setDismissedId(abandonedInterrupt.id)}\n className=\"shrink-0 rounded-md p-1 hover:bg-amber-100\"\n >\n <XIcon className=\"size-3.5\" />\n </button>\n </div>\n );\n }\n\n const context: StatewireApprovalRequestContext = { request, hitl, actionRequests };\n if (isHandledElsewhere?.(context)) return null;\n\n const message = readInterruptMessage(request.payload, fallbackMessage);\n const toolName = hitl?.context?.tool_name ?? hitl?.context?.tool_id;\n const pendingCount = hitl?.context?.pending_action_count ?? 1;\n const hasDetails = hitl !== null && actionRequests.length > 0;\n const showDetails = hasDetails && (detailsOverride ?? actionRequests.length <= 1);\n\n return (\n <section\n aria-label={hitl ? 'Approval required' : 'Input required'}\n className={cn(\n 'aui-approval-card w-full rounded-2xl border border-amber-300 bg-amber-50 px-4 py-3 text-amber-900',\n classNames?.root,\n )}\n >\n <div className=\"flex items-start gap-3\">\n <TriangleAlertIcon className=\"mt-0.5 size-4 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"flex items-center gap-2 font-semibold text-sm\">\n <span>{hitl ? 'Approval required' : 'Waiting for input'}</span>\n {hitl && pendingCount > 1 ? (\n <span className=\"shrink-0 font-normal text-amber-800 text-xs\">\n {pendingCount} tool calls\n </span>\n ) : null}\n </p>\n <p className=\"pt-1 text-amber-800 text-sm leading-snug\">\n {hitl && pendingCount === 1 && toolName ? (\n <>\n <span className=\"font-mono\">{toolName}</span> requires approval before it runs.\n </>\n ) : (\n message\n )}\n </p>\n {hasDetails ? (\n <button\n type=\"button\"\n onClick={() => setDetailsOverride(!showDetails)}\n aria-expanded={showDetails}\n className=\"mt-2 flex items-center gap-1 rounded px-1 py-0.5 text-amber-800 text-xs transition-colors hover:text-amber-950\"\n >\n <ChevronDownIcon\n className={cn('size-3 transition-transform', showDetails && 'rotate-180')}\n />\n {showDetails ? 'Hide details' : 'Review details'}\n </button>\n ) : null}\n {/* EVERY pending gated call renders, in order — one decision\n authorizes the whole batch, so nothing may hide behind a host's\n richer cards. All read-only: this runtime resumes through\n `hitl_response_to_decisions`, which has no `tool_args` channel —\n an editable composer would let someone rewrite an email and then\n watch the original go out. */}\n {showDetails ? (\n <div className={cn('mt-2 flex flex-col gap-2', classNames?.details)}>\n {renderDetails ? (\n renderDetails(context)\n ) : (\n <DefaultApprovalDetails actionRequests={actionRequests} />\n )}\n </div>\n ) : null}\n </div>\n </div>\n <div className={cn('flex flex-wrap items-center gap-2 pt-3', classNames?.actions)}>\n {readOnly ? (\n <p className=\"text-amber-800 text-sm\" data-testid=\"statewire-approval-read-only\">\n {readOnlyNotice}\n </p>\n ) : hitl ? (\n <>\n <Button\n size=\"sm\"\n disabled={pending}\n onClick={() => resume(hitlResumeApprove())}\n className=\"bg-green-600 text-white hover:bg-green-700\"\n >\n <CheckIcon />\n Approve\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={pending}\n onClick={() => resume(hitlResumeReject())}\n >\n <XIcon />\n Reject\n </Button>\n </>\n ) : (\n <>\n <p className=\"text-amber-800 text-sm\" data-testid=\"statewire-approval-non-hitl\">\n {nonHitlNotice}\n </p>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={pending}\n onClick={() => resume(interruptResumeDecline())}\n >\n <XIcon />\n Decline\n </Button>\n </>\n )}\n </div>\n </section>\n );\n};\n\n/** One block per gated call: its name (when the batch has several) and its arguments as JSON. */\nconst DefaultApprovalDetails: FC<{ actionRequests: PendingActionRequest[] }> = ({\n actionRequests,\n}) => (\n <>\n {actionRequests.map((action, index) => (\n <div\n key={`${action.name}-${index}`}\n className=\"rounded-lg bg-amber-100 p-2 text-amber-900 text-xs leading-relaxed\"\n >\n {actionRequests.length > 1 ? (\n <p className=\"font-mono font-semibold\">{action.name}</p>\n ) : null}\n {Object.keys(action.args).length > 0 ? (\n <pre className=\"mt-1 max-h-32 overflow-auto whitespace-pre-wrap break-words rounded bg-white/60 p-1.5 font-mono text-[11px]\">\n {JSON.stringify(action.args, null, 2)}\n </pre>\n ) : (\n <p className=\"text-amber-800\">No arguments.</p>\n )}\n </div>\n ))}\n </>\n);\n"],"names":["useAuiState","hasStatewireThreadExtras","jsx","useStatewireApprovalRequest","useState","useRef","jsxs","cn","XIcon","readInterruptMessage","TriangleAlertIcon","Fragment","ChevronDownIcon","Button","hitlResumeApprove","CheckIcon","hitlResumeReject","interruptResumeDecline"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA6GO,MAAM,wBAAwD,CAAC,UAAU;AAC9E,QAAM,SAASA,MAAAA,YAAY,CAAC,MAAM,EAAE,OAAO,MAAM;AACjD,MAAI,CAACC,aAAAA,yBAAyB,MAAM,EAAG,QAAO;AAC9C,SAAOC,+BAAC,4BAAA,EAA4B,GAAG,MAAA,CAAO;AAChD;AAEA,MAAM,6BAA6D,CAAC;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB;AACF,MAAM;;AACJ,QAAM,EAAE,SAAS,WAAW,MAAM,gBAAgB,oBAAoB,SAAS,OAAA,IAC7EC,4BAAAA,4BAA4B,EAAE,oBAAoB,sBAAsB,kBAAkB;AAC5F,QAAM,CAAC,aAAa,cAAc,IAAIC,iBAAA;AAItC,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,iBAAA;AAK9C,QAAM,mBAAmBC,QAAAA,OAAO,SAAS;AACzC,MAAI,iBAAiB,YAAY,WAAW;AAC1C,qBAAiB,UAAU;AAC3B,QAAI,oBAAoB,OAAW,oBAAmB,MAAS;AAAA,EACjE;AAEA,MAAI,CAAC,SAAS;AACZ,QAAI,CAAC,sBAAsB,mBAAmB,OAAO,YAAa,QAAO;AACzE,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC,OAAAA;AAAAA,UACT;AAAA,UACA,yCAAY;AAAA,QAAA;AAAA,QAGd,UAAA;AAAA,UAAAL,2BAAAA,IAAC,OAAE,UAAA,6CAAA,CAA0C;AAAA,UAC7CA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,SAAS,MAAM,eAAe,mBAAmB,EAAE;AAAA,cACnD,WAAU;AAAA,cAEV,UAAAA,2BAAAA,IAACM,YAAAA,OAAA,EAAM,WAAU,WAAA,CAAW;AAAA,YAAA;AAAA,UAAA;AAAA,QAC9B;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,QAAM,UAA2C,EAAE,SAAS,MAAM,eAAA;AAClE,MAAI,yDAAqB,SAAU,QAAO;AAE1C,QAAM,UAAUC,WAAAA,qBAAqB,QAAQ,SAAS,eAAe;AACrE,QAAM,aAAW,kCAAM,YAAN,mBAAe,gBAAa,kCAAM,YAAN,mBAAe;AAC5D,QAAM,iBAAe,kCAAM,YAAN,mBAAe,yBAAwB;AAC5D,QAAM,aAAa,SAAS,QAAQ,eAAe,SAAS;AAC5D,QAAM,cAAc,eAAe,mBAAmB,eAAe,UAAU;AAE/E,SACEH,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAY,OAAO,sBAAsB;AAAA,MACzC,WAAWC,OAAAA;AAAAA,QACT;AAAA,QACA,yCAAY;AAAA,MAAA;AAAA,MAGd,UAAA;AAAA,QAAAD,2BAAAA,KAAC,OAAA,EAAI,WAAU,0BACb,UAAA;AAAA,UAAAJ,2BAAAA,IAACQ,YAAAA,mBAAA,EAAkB,WAAU,yBAAA,CAAyB;AAAA,UACtDJ,2BAAAA,KAAC,OAAA,EAAI,WAAU,kBACb,UAAA;AAAA,YAAAA,2BAAAA,KAAC,KAAA,EAAE,WAAU,iDACX,UAAA;AAAA,cAAAJ,2BAAAA,IAAC,QAAA,EAAM,UAAA,OAAO,sBAAsB,qBAAoB;AAAA,cACvD,QAAQ,eAAe,IACtBI,2BAAAA,KAAC,QAAA,EAAK,WAAU,+CACb,UAAA;AAAA,gBAAA;AAAA,gBAAa;AAAA,cAAA,EAAA,CAChB,IACE;AAAA,YAAA,GACN;AAAA,YACAJ,2BAAAA,IAAC,OAAE,WAAU,4CACV,kBAAQ,iBAAiB,KAAK,WAC7BI,2BAAAA,KAAAK,WAAAA,UAAA,EACE,UAAA;AAAA,cAAAT,2BAAAA,IAAC,QAAA,EAAK,WAAU,aAAa,UAAA,UAAS;AAAA,cAAO;AAAA,YAAA,EAAA,CAC/C,IAEA,SAEJ;AAAA,YACC,aACCI,2BAAAA;AAAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,mBAAmB,CAAC,WAAW;AAAA,gBAC9C,iBAAe;AAAA,gBACf,WAAU;AAAA,gBAEV,UAAA;AAAA,kBAAAJ,2BAAAA;AAAAA,oBAACU,YAAAA;AAAAA,oBAAA;AAAA,sBACC,WAAWL,OAAAA,GAAG,+BAA+B,eAAe,YAAY;AAAA,oBAAA;AAAA,kBAAA;AAAA,kBAEzE,cAAc,iBAAiB;AAAA,gBAAA;AAAA,cAAA;AAAA,YAAA,IAEhC;AAAA,YAOH,cACCL,2BAAAA,IAAC,OAAA,EAAI,WAAWK,OAAAA,GAAG,4BAA4B,yCAAY,OAAO,GAC/D,UAAA,gBACC,cAAc,OAAO,mCAEpB,wBAAA,EAAuB,gBAAgC,GAE5D,IACE;AAAA,UAAA,EAAA,CACN;AAAA,QAAA,GACF;AAAA,uCACC,OAAA,EAAI,WAAWA,UAAG,0CAA0C,yCAAY,OAAO,GAC7E,UAAA,WACCL,2BAAAA,IAAC,KAAA,EAAE,WAAU,0BAAyB,eAAY,gCAC/C,UAAA,gBACH,IACE,OACFI,2BAAAA,KAAAK,WAAAA,UAAA,EACE,UAAA;AAAA,UAAAL,2BAAAA;AAAAA,YAACO,OAAAA;AAAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,UAAU;AAAA,cACV,SAAS,MAAM,OAAOC,WAAAA,mBAAmB;AAAA,cACzC,WAAU;AAAA,cAEV,UAAA;AAAA,gBAAAZ,2BAAAA,IAACa,YAAAA,WAAA,EAAU;AAAA,gBAAE;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGfT,2BAAAA;AAAAA,YAACO,OAAAA;AAAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,UAAU;AAAA,cACV,SAAS,MAAM,OAAOG,WAAAA,kBAAkB;AAAA,cAExC,UAAA;AAAA,gBAAAd,2BAAAA,IAACM,YAAAA,OAAA,EAAM;AAAA,gBAAE;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QAEX,EAAA,CACF,IAEAF,2BAAAA,KAAAK,WAAAA,UAAA,EACE,UAAA;AAAA,UAAAT,+BAAC,KAAA,EAAE,WAAU,0BAAyB,eAAY,+BAC/C,UAAA,eACH;AAAA,UACAI,2BAAAA;AAAAA,YAACO,OAAAA;AAAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,UAAU;AAAA,cACV,SAAS,MAAM,OAAOI,WAAAA,wBAAwB;AAAA,cAE9C,UAAA;AAAA,gBAAAf,2BAAAA,IAACM,YAAAA,OAAA,EAAM;AAAA,gBAAE;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QAEX,EAAA,CACF,EAAA,CAEJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;AAGA,MAAM,yBAAyE,CAAC;AAAA,EAC9E;AACF,MACEN,2BAAAA,IAAAS,WAAAA,UAAA,EACG,UAAA,eAAe,IAAI,CAAC,QAAQ,UAC3BL,2BAAAA;AAAAA,EAAC;AAAA,EAAA;AAAA,IAEC,WAAU;AAAA,IAET,UAAA;AAAA,MAAA,eAAe,SAAS,IACvBJ,2BAAAA,IAAC,KAAA,EAAE,WAAU,2BAA2B,UAAA,OAAO,MAAK,IAClD;AAAA,MACH,OAAO,KAAK,OAAO,IAAI,EAAE,SAAS,IACjCA,2BAAAA,IAAC,OAAA,EAAI,WAAU,+GACZ,eAAK,UAAU,OAAO,MAAM,MAAM,CAAC,EAAA,CACtC,IAEAA,+BAAC,KAAA,EAAE,WAAU,kBAAiB,UAAA,gBAAA,CAAa;AAAA,IAAA;AAAA,EAAA;AAAA,EAXxC,GAAG,OAAO,IAAI,IAAI,KAAK;AAa9B,CACD,GACH;;"}
|
|
@@ -73,5 +73,19 @@ export interface StatewireApprovalCardProps {
|
|
|
73
73
|
*/
|
|
74
74
|
readOnly?: boolean;
|
|
75
75
|
readOnlyNotice?: ReactNode;
|
|
76
|
+
/**
|
|
77
|
+
* Copy for a park that is not a HITL approval — a typed Athena interrupt
|
|
78
|
+
* this surface has no card for. The only honest action is to decline it
|
|
79
|
+
* (`{ action: 'reject' }`, which every typed raiser treats as a clean user
|
|
80
|
+
* decline); a generic "Continue" would be rejected by the raiser's resume
|
|
81
|
+
* schema and surface as a tool error.
|
|
82
|
+
*/
|
|
83
|
+
nonHitlNotice?: ReactNode;
|
|
84
|
+
/**
|
|
85
|
+
* The host refused a decision (or the transport lost it). The optimistic
|
|
86
|
+
* fold has rolled back and the request is on screen again with its actions
|
|
87
|
+
* re-enabled; this is where a host tells the user why (a toast, a log line).
|
|
88
|
+
*/
|
|
89
|
+
onResumeRejected?: (error: unknown, requestId: string) => void;
|
|
76
90
|
}
|
|
77
91
|
export declare const StatewireApprovalCard: FC<StatewireApprovalCardProps>;
|
|
@@ -6,16 +6,17 @@ import "@assistant-ui/tap";
|
|
|
6
6
|
import "statewire";
|
|
7
7
|
import "../packages/agent-runtime/src/statewire-suspended.js";
|
|
8
8
|
import "@tanstack/react-query";
|
|
9
|
-
import { readInterruptMessage, hitlResumeApprove, hitlResumeReject,
|
|
9
|
+
import { readInterruptMessage, hitlResumeApprove, hitlResumeReject, interruptResumeDecline } from "../packages/agent-runtime/src/interrupts.js";
|
|
10
10
|
import "../packages/agent-runtime/src/messages.js";
|
|
11
11
|
import "../packages/agent-runtime/src/statewire-state.js";
|
|
12
12
|
import { hasStatewireThreadExtras } from "../packages/agent-runtime/src/thread-extras.js";
|
|
13
13
|
import "@assistant-ui/react-statewire";
|
|
14
14
|
import "@assistant-ui/store";
|
|
15
|
-
import "
|
|
15
|
+
import "../packages/agent-runtime/src/quota-safe-local-storage-session.js";
|
|
16
16
|
import "../packages/agent-runtime/src/statewire-attach-observer.js";
|
|
17
17
|
import "../packages/agent-runtime/src/thread-status.js";
|
|
18
18
|
import "@assistant-ui/core";
|
|
19
|
+
import "../packages/agent-runtime/src/thread-handoff.js";
|
|
19
20
|
import { XIcon, TriangleAlertIcon, ChevronDownIcon, CheckIcon } from "lucide-react";
|
|
20
21
|
import { Button } from "../ui/button.js";
|
|
21
22
|
import { useStatewireApprovalRequest } from "./use-statewire-approval-request.js";
|
|
@@ -33,10 +34,12 @@ const StatewireApprovalCardInner = ({
|
|
|
33
34
|
isHandledElsewhere,
|
|
34
35
|
renderDetails,
|
|
35
36
|
readOnly = false,
|
|
36
|
-
readOnlyNotice = "Waiting for someone with edit access to respond."
|
|
37
|
+
readOnlyNotice = "Waiting for someone with edit access to respond.",
|
|
38
|
+
nonHitlNotice = "This request needs an answer this surface cannot collect.",
|
|
39
|
+
onResumeRejected
|
|
37
40
|
}) => {
|
|
38
41
|
var _a, _b, _c;
|
|
39
|
-
const { request, requestId, hitl, actionRequests, abandonedInterrupt, pending, resume } = useStatewireApprovalRequest({ interruptPredicate, pendingResumeGraceMs });
|
|
42
|
+
const { request, requestId, hitl, actionRequests, abandonedInterrupt, pending, resume } = useStatewireApprovalRequest({ interruptPredicate, pendingResumeGraceMs, onResumeRejected });
|
|
40
43
|
const [dismissedId, setDismissedId] = useState();
|
|
41
44
|
const [detailsOverride, setDetailsOverride] = useState();
|
|
42
45
|
const prevRequestIdRef = useRef(requestId);
|
|
@@ -147,7 +150,22 @@ const StatewireApprovalCardInner = ({
|
|
|
147
150
|
]
|
|
148
151
|
}
|
|
149
152
|
)
|
|
150
|
-
] }) : /* @__PURE__ */
|
|
153
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
154
|
+
/* @__PURE__ */ jsx("p", { className: "text-amber-800 text-sm", "data-testid": "statewire-approval-non-hitl", children: nonHitlNotice }),
|
|
155
|
+
/* @__PURE__ */ jsxs(
|
|
156
|
+
Button,
|
|
157
|
+
{
|
|
158
|
+
size: "sm",
|
|
159
|
+
variant: "outline",
|
|
160
|
+
disabled: pending,
|
|
161
|
+
onClick: () => resume(interruptResumeDecline()),
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ jsx(XIcon, {}),
|
|
164
|
+
"Decline"
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
] }) })
|
|
151
169
|
]
|
|
152
170
|
}
|
|
153
171
|
);
|