@athenaintel/react 0.23.3 → 0.24.1
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 +17 -4
- package/dist/assets/AssetPanel.cjs +1 -1
- package/dist/assets/AssetPanel.cjs.map +1 -1
- package/dist/assets/AssetPanel.js +1 -1
- package/dist/assets/AssetPanel.js.map +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
- package/dist/assets/AthenaAssetEmbed.js +1 -1
- package/dist/assets/AthenaAssetEmbed.js.map +1 -1
- package/dist/chat/AthenaChat.cjs +15 -23
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +9 -4
- package/dist/chat/AthenaChat.js +15 -23
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireApprovalCard.cjs +34 -24
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.d.ts +34 -4
- package/dist/chat/StatewireApprovalCard.js +34 -24
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/StatewireClientToolBridge.cjs +2 -16
- package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
- package/dist/chat/StatewireClientToolBridge.js +2 -16
- package/dist/chat/StatewireClientToolBridge.js.map +1 -1
- package/dist/chat/StatewireQueuedMessages.cjs +5 -16
- package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
- package/dist/chat/StatewireQueuedMessages.js +5 -16
- package/dist/chat/StatewireQueuedMessages.js.map +1 -1
- package/dist/chat/WaitingOnYouBanner.cjs +3 -18
- package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
- package/dist/chat/WaitingOnYouBanner.js +3 -18
- package/dist/chat/WaitingOnYouBanner.js.map +1 -1
- package/dist/chat/run-activity.cjs +0 -16
- package/dist/chat/run-activity.cjs.map +1 -1
- package/dist/chat/run-activity.js +0 -16
- package/dist/chat/run-activity.js.map +1 -1
- package/dist/chat/statewire-client-tools.cjs +1 -17
- package/dist/chat/statewire-client-tools.cjs.map +1 -1
- package/dist/chat/statewire-client-tools.js +1 -17
- package/dist/chat/statewire-client-tools.js.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.cjs +0 -16
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.js +0 -16
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/tiptap-text.cjs +1 -17
- package/dist/chat/tiptap-text.cjs.map +1 -1
- package/dist/chat/tiptap-text.js +1 -17
- package/dist/chat/tiptap-text.js.map +1 -1
- package/dist/chat/use-athena-run-live.cjs +0 -15
- package/dist/chat/use-athena-run-live.cjs.map +1 -1
- package/dist/chat/use-athena-run-live.js +0 -15
- package/dist/chat/use-athena-run-live.js.map +1 -1
- package/dist/chat/use-statewire-approval-request.cjs +4 -16
- package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
- package/dist/chat/use-statewire-approval-request.d.ts +2 -0
- package/dist/chat/use-statewire-approval-request.js +4 -16
- package/dist/chat/use-statewire-approval-request.js.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.cjs +1 -16
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.js +1 -16
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/chat-ui/index.cjs +0 -16
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +1 -1
- package/dist/chat-ui/index.js +0 -16
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.cjs +1 -16
- package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.js +1 -16
- package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs +4 -6
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js +4 -6
- package/dist/chat-ui/mentions/suggestions/data/sources/asset-type-icons.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +2 -9
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js +2 -9
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
- package/dist/chat-ui/messages/message-components.cjs +5 -19
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.js +5 -19
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/chat-ui/messages/runtime-message.cjs +1 -16
- package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
- package/dist/chat-ui/messages/runtime-message.js +1 -16
- package/dist/chat-ui/messages/runtime-message.js.map +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.cjs +3 -15
- package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.js +3 -15
- package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.cjs +0 -16
- package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
- package/dist/chat-ui/messages/user-edit-composer.js +0 -16
- package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
- package/dist/chat-ui/messages/wake-marker.cjs +1 -16
- package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
- package/dist/chat-ui/messages/wake-marker.js +1 -16
- package/dist/chat-ui/messages/wake-marker.js.map +1 -1
- package/dist/chat-ui/statewire.cjs +0 -16
- package/dist/chat-ui/statewire.cjs.map +1 -1
- package/dist/chat-ui/statewire.d.ts +1 -1
- package/dist/chat-ui/statewire.js +0 -16
- package/dist/chat-ui/statewire.js.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -17
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.js +2 -17
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/dictation/DictationStrip.cjs +2 -1
- package/dist/dictation/DictationStrip.cjs.map +1 -1
- package/dist/dictation/DictationStrip.d.ts +2 -0
- package/dist/dictation/DictationStrip.js +2 -1
- package/dist/dictation/DictationStrip.js.map +1 -1
- package/dist/dictation/dictation-engine.cjs.map +1 -1
- package/dist/dictation/dictation-engine.d.ts +8 -0
- package/dist/dictation/dictation-engine.js.map +1 -1
- package/dist/dictation/recorded-dictation-engine.cjs +153 -0
- package/dist/dictation/recorded-dictation-engine.cjs.map +1 -0
- package/dist/dictation/recorded-dictation-engine.d.ts +66 -0
- package/dist/dictation/recorded-dictation-engine.js +153 -0
- package/dist/dictation/recorded-dictation-engine.js.map +1 -0
- package/dist/dictation/use-athena-dictation-engine.cjs +64 -0
- package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -0
- package/dist/dictation/use-athena-dictation-engine.d.ts +25 -0
- package/dist/dictation/use-athena-dictation-engine.js +64 -0
- package/dist/dictation/use-athena-dictation-engine.js.map +1 -0
- package/dist/dictation/use-dictation.cjs +17 -6
- package/dist/dictation/use-dictation.cjs.map +1 -1
- package/dist/dictation/use-dictation.d.ts +20 -4
- package/dist/dictation/use-dictation.js +17 -6
- package/dist/dictation/use-dictation.js.map +1 -1
- package/dist/index.cjs +29 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +12 -22
- package/dist/index.js.map +1 -1
- package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
- 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/errors.cjs +18 -0
- package/dist/packages/agent-runtime/src/errors.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/errors.js +19 -1
- package/dist/packages/agent-runtime/src/errors.js.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +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 +83 -14
- package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.js +83 -14
- package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/packages/agent-runtime/src/messages.cjs +2 -1
- package/dist/packages/agent-runtime/src/messages.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/messages.js +2 -1
- package/dist/packages/agent-runtime/src/messages.js.map +1 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +49 -0
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +49 -0
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
- package/dist/packages/agent-runtime/src/thread-status.js +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.cjs +1 -37
- package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.js +1 -37
- package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/packages/agent-runtime/src/voice-presentation.cjs +6 -0
- package/dist/packages/agent-runtime/src/voice-presentation.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/voice-presentation.js +6 -0
- package/dist/packages/agent-runtime/src/voice-presentation.js.map +1 -1
- package/dist/packages/agent-runtime/src/voice-turn-grouping.cjs +68 -0
- package/dist/packages/agent-runtime/src/voice-turn-grouping.cjs.map +1 -0
- package/dist/packages/agent-runtime/src/voice-turn-grouping.js +68 -0
- package/dist/packages/agent-runtime/src/voice-turn-grouping.js.map +1 -0
- package/dist/packages/react/package.json.cjs +1 -1
- package/dist/packages/react/package.json.js +1 -1
- package/dist/provider/AthenaProvider.cjs +5 -18
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.js +5 -18
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/theme-style-context.cjs +12 -0
- package/dist/provider/theme-style-context.cjs.map +1 -0
- package/dist/provider/theme-style-context.d.ts +8 -0
- package/dist/provider/theme-style-context.js +12 -0
- package/dist/provider/theme-style-context.js.map +1 -0
- package/dist/runtime/statewire-lifecycle.cjs +0 -16
- package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
- package/dist/runtime/statewire-lifecycle.js +0 -16
- package/dist/runtime/statewire-lifecycle.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +3 -17
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +3 -17
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/threads/SessionList.cjs +87 -16
- package/dist/threads/SessionList.cjs.map +1 -1
- package/dist/threads/SessionList.js +88 -17
- package/dist/threads/SessionList.js.map +1 -1
- package/dist/threads/session-list-model.cjs +13 -5
- package/dist/threads/session-list-model.cjs.map +1 -1
- package/dist/threads/session-list-model.d.ts +10 -3
- package/dist/threads/session-list-model.js +13 -5
- package/dist/threads/session-list-model.js.map +1 -1
- package/dist/threads/statewire-thread-list.cjs +4 -17
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.js +4 -17
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/threads/use-rest-conversation-history.cjs +2 -16
- package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
- package/dist/threads/use-rest-conversation-history.js +2 -16
- package/dist/threads/use-rest-conversation-history.js.map +1 -1
- package/dist/threads/use-statewire-conversation-history.cjs +2 -16
- package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
- package/dist/threads/use-statewire-conversation-history.js +2 -16
- package/dist/threads/use-statewire-conversation-history.js.map +1 -1
- package/dist/toolkits/index.cjs +0 -7
- package/dist/toolkits/index.cjs.map +1 -1
- package/dist/toolkits/index.d.ts +0 -2
- package/dist/toolkits/index.js +0 -7
- package/dist/toolkits/index.js.map +1 -1
- package/dist/tools/index.cjs +4 -20
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +1 -17
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/misc-tool-uis.cjs +2 -17
- package/dist/tools/misc-tool-uis.cjs.map +1 -1
- package/dist/tools/misc-tool-uis.js +2 -17
- package/dist/tools/misc-tool-uis.js.map +1 -1
- package/dist/tools/primitives.cjs +1 -16
- package/dist/tools/primitives.cjs.map +1 -1
- package/dist/tools/primitives.js +1 -16
- package/dist/tools/primitives.js.map +1 -1
- package/dist/tools/tool-fallback.cjs +2 -17
- package/dist/tools/tool-fallback.cjs.map +1 -1
- package/dist/tools/tool-fallback.js +2 -17
- package/dist/tools/tool-fallback.js.map +1 -1
- package/dist/tools/voice-update-tool-ui.cjs +1 -17
- package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
- package/dist/tools/voice-update-tool-ui.js +1 -17
- package/dist/tools/voice-update-tool-ui.js.map +1 -1
- package/dist/ui/popover.cjs +46 -0
- package/dist/ui/popover.cjs.map +1 -0
- package/dist/ui/popover.d.ts +6 -0
- package/dist/ui/popover.js +46 -0
- package/dist/ui/popover.js.map +1 -0
- package/package.json +2 -2
- package/dist/node_modules/qrcode-generator/dist/qrcode.cjs +0 -32
- package/dist/node_modules/qrcode-generator/dist/qrcode.cjs.map +0 -1
- package/dist/node_modules/qrcode-generator/dist/qrcode.js +0 -31
- package/dist/node_modules/qrcode-generator/dist/qrcode.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/errors.cjs +0 -44
- package/dist/node_modules/zod/v4/classic/errors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/errors.js +0 -44
- package/dist/node_modules/zod/v4/classic/errors.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/iso.cjs +0 -43
- package/dist/node_modules/zod/v4/classic/iso.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/iso.js +0 -43
- package/dist/node_modules/zod/v4/classic/iso.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/parse.cjs +0 -29
- package/dist/node_modules/zod/v4/classic/parse.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/parse.js +0 -29
- package/dist/node_modules/zod/v4/classic/parse.js.map +0 -1
- package/dist/node_modules/zod/v4/classic/schemas.cjs +0 -871
- package/dist/node_modules/zod/v4/classic/schemas.cjs.map +0 -1
- package/dist/node_modules/zod/v4/classic/schemas.js +0 -871
- package/dist/node_modules/zod/v4/classic/schemas.js.map +0 -1
- package/dist/node_modules/zod/v4/core/api.cjs +0 -570
- package/dist/node_modules/zod/v4/core/api.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/api.js +0 -570
- package/dist/node_modules/zod/v4/core/api.js.map +0 -1
- package/dist/node_modules/zod/v4/core/checks.cjs +0 -411
- package/dist/node_modules/zod/v4/core/checks.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/checks.js +0 -411
- package/dist/node_modules/zod/v4/core/checks.js.map +0 -1
- package/dist/node_modules/zod/v4/core/core.cjs +0 -77
- package/dist/node_modules/zod/v4/core/core.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/core.js +0 -77
- package/dist/node_modules/zod/v4/core/core.js.map +0 -1
- package/dist/node_modules/zod/v4/core/doc.cjs +0 -38
- package/dist/node_modules/zod/v4/core/doc.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/doc.js +0 -38
- package/dist/node_modules/zod/v4/core/doc.js.map +0 -1
- package/dist/node_modules/zod/v4/core/errors.cjs +0 -76
- package/dist/node_modules/zod/v4/core/errors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/errors.js +0 -76
- package/dist/node_modules/zod/v4/core/errors.js.map +0 -1
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +0 -312
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/json-schema-processors.js +0 -312
- package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +0 -1
- package/dist/node_modules/zod/v4/core/parse.cjs +0 -96
- package/dist/node_modules/zod/v4/core/parse.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/parse.js +0 -96
- package/dist/node_modules/zod/v4/core/parse.js.map +0 -1
- package/dist/node_modules/zod/v4/core/regexes.cjs +0 -86
- package/dist/node_modules/zod/v4/core/regexes.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/regexes.js +0 -86
- package/dist/node_modules/zod/v4/core/regexes.js.map +0 -1
- package/dist/node_modules/zod/v4/core/registries.cjs +0 -52
- package/dist/node_modules/zod/v4/core/registries.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/registries.js +0 -52
- package/dist/node_modules/zod/v4/core/registries.js.map +0 -1
- package/dist/node_modules/zod/v4/core/schemas.cjs +0 -1412
- package/dist/node_modules/zod/v4/core/schemas.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/schemas.js +0 -1413
- package/dist/node_modules/zod/v4/core/schemas.js.map +0 -1
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs +0 -370
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/to-json-schema.js +0 -370
- package/dist/node_modules/zod/v4/core/to-json-schema.js.map +0 -1
- package/dist/node_modules/zod/v4/core/util.cjs +0 -444
- package/dist/node_modules/zod/v4/core/util.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/util.js +0 -444
- package/dist/node_modules/zod/v4/core/util.js.map +0 -1
- package/dist/node_modules/zod/v4/core/versions.cjs +0 -9
- package/dist/node_modules/zod/v4/core/versions.cjs.map +0 -1
- package/dist/node_modules/zod/v4/core/versions.js +0 -9
- package/dist/node_modules/zod/v4/core/versions.js.map +0 -1
- package/dist/packages/agent-runtime/src/thread-handoff.cjs +0 -85
- package/dist/packages/agent-runtime/src/thread-handoff.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/thread-handoff.js +0 -85
- package/dist/packages/agent-runtime/src/thread-handoff.js.map +0 -1
|
@@ -1 +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 canApproveForSession,\n fanLaneDecision,\n hasStatewireThreadExtras,\n type HitlApprovalPayload,\n hitlResumeApprove,\n hitlResumeApproveForSession,\n hitlResumeReject,\n interruptResumeDecline,\n laneInterruptIds,\n type PendingActionRequest,\n readInterruptMessage,\n} from '@athenaintel/agent-runtime';\nimport {\n CheckCheckIcon,\n CheckIcon,\n ChevronDownIcon,\n TriangleAlertIcon,\n XIcon,\n} from 'lucide-react';\nimport { type FC, type ReactNode, useCallback, 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 * A gated call raised inside a delegated subagent parks only its lane: the\n * host mirrors that interrupt while the sibling lanes keep streaming, before\n * any input request exists. The card renders the mirrored payload the same\n * way and answers it with `run/lane-input` keyed by the lane's langgraph\n * interrupt id (`laneInterruptIds`); the host queues the decision and applies\n * it the moment the run parks, so the approver never waits on the siblings.\n * Once parked, a payload whose calls all carry interrupt ids resumes as a\n * `lane_decisions` map too — one positional `run/input` can only answer the\n * first of several parked lanes.\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 /** The parked input request — `undefined` while the run still streams (a live lane approval). */\n request: StatewireThread.CustomInputRequest | undefined;\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 {\n request,\n requestId,\n hitl: parkedHitl,\n actionRequests: parkedActionRequests,\n liveHitl,\n liveActionRequests,\n queuedLaneDecisions,\n abandonedInterrupt,\n pending,\n resume,\n sendLaneDecisions,\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 // The live lanes a decision was sent for (joined ids); the buttons stay\n // locked until the host echoes them in `queuedLaneDecisions` or refuses.\n const [liveSentKey, setLiveSentKey] = useState<string>();\n\n const isLive = request === undefined && liveHitl !== null;\n const hitl = isLive ? liveHitl : parkedHitl;\n const actionRequests = isLive ? liveActionRequests : parkedActionRequests;\n const laneIds = laneInterruptIds(actionRequests);\n const undecidedLaneIds = laneIds?.filter((id) => !(id in queuedLaneDecisions)) ?? null;\n const liveKey = isLive ? (laneIds?.join('|') ?? null) : null;\n const viewKey = requestId ?? liveKey ?? undefined;\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 prevViewKeyRef = useRef(viewKey);\n if (prevViewKeyRef.current !== viewKey) {\n prevViewKeyRef.current = viewKey;\n if (detailsOverride !== undefined) setDetailsOverride(undefined);\n if (liveSentKey !== undefined) setLiveSentKey(undefined);\n }\n\n const decide = useCallback(\n (value: Record<string, string>) => {\n // Several parked lanes (or a live one) answer by interrupt id: a\n // positional `run/input` would resume only the first and re-park the\n // rest. A lone parked lane keeps the positional resume every host\n // already speaks.\n if (laneIds === null || undecidedLaneIds === null || (!isLive && laneIds.length <= 1)) {\n resume(value);\n return;\n }\n const lanes = fanLaneDecision({ interruptIds: undecidedLaneIds, decision: value });\n if (!isLive) {\n sendLaneDecisions(lanes);\n return;\n }\n const key = undecidedLaneIds.join('|');\n setLiveSentKey(key);\n Promise.resolve(sendLaneDecisions(lanes)).catch((error: unknown) => {\n setLiveSentKey((current) => (current === key ? undefined : current));\n onResumeRejected?.(error, undecidedLaneIds[0] ?? '');\n });\n },\n [laneIds, undecidedLaneIds, isLive, resume, sendLaneDecisions, onResumeRejected],\n );\n\n if (!request && !isLive) {\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 // A live mirror without lane stamps cannot be answered before the run\n // parks; the parked request renders it then.\n if (isLive && laneIds === null) return null;\n\n const message = readInterruptMessage(request ? request.payload : liveHitl, 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 const sessionApprovalOffered = hitl !== null && canApproveForSession(actionRequests);\n const laneDecided =\n undecidedLaneIds !== null &&\n laneIds !== null &&\n (undecidedLaneIds.length === 0 || liveSentKey === undecidedLaneIds.join('|'));\n const locked = pending || laneDecided;\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 {isLive ? (\n <p className=\"pt-1 text-amber-700 text-xs\" data-testid=\"statewire-approval-live\">\n {laneDecided\n ? 'Decision sent — it applies as soon as this step pauses.'\n : 'Other work keeps running while you decide.'}\n </p>\n ) : null}\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={locked}\n onClick={() => decide(hitlResumeApprove())}\n className=\"bg-green-600 text-white hover:bg-green-700\"\n >\n <CheckIcon />\n Approve\n </Button>\n {sessionApprovalOffered ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={locked}\n onClick={() => decide(hitlResumeApproveForSession())}\n data-testid=\"statewire-approval-approve-session\"\n >\n <CheckCheckIcon />\n Approve all for this session\n </Button>\n ) : null}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={locked}\n onClick={() => decide(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","laneInterruptIds","useRef","useCallback","fanLaneDecision","jsxs","cn","XIcon","readInterruptMessage","canApproveForSession","TriangleAlertIcon","Fragment","ChevronDownIcon","Button","hitlResumeApprove","CheckIcon","hitlResumeApproveForSession","CheckCheckIcon","hitlResumeReject","interruptResumeDecline"],"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 actionRequestToolCallIds,\n canApproveForSession,\n fanLaneDecision,\n hasStatewireThreadExtras,\n type HitlActionDecision,\n type HitlApprovalPayload,\n hitlResumeActionDecisions,\n hitlResumeApprove,\n hitlResumeApproveForSession,\n hitlResumeReject,\n interruptResumeDecline,\n laneInterruptIds,\n type PendingActionRequest,\n readInterruptMessage,\n} from '@athenaintel/agent-runtime';\nimport {\n CheckCheckIcon,\n CheckIcon,\n ChevronDownIcon,\n TriangleAlertIcon,\n XIcon,\n} from 'lucide-react';\nimport { type FC, type ReactNode, useCallback, useMemo, 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 * A gated call raised inside a delegated subagent parks only its lane: the\n * host mirrors that interrupt while the sibling lanes keep streaming, before\n * any input request exists. The card renders the mirrored payload the same\n * way and answers it with `run/lane-input` keyed by the lane's langgraph\n * interrupt id (`laneInterruptIds`); the host queues the decision and applies\n * it the moment the run parks, so the approver never waits on the siblings.\n * Once parked, a payload whose calls all carry interrupt ids resumes as a\n * `lane_decisions` map too — one positional `run/input` can only answer the\n * first of several parked lanes.\n *\n * Host-specific review UIs (subagent cards, email summaries) slot in through\n * `renderDetails`; a host composer that must also OWN the decision (an\n * editable email whose edits have to reach the backend) takes over the\n * details and the decision row through `renderEditor`; requests another\n * surface answers (typed interrupt families, per-lane subagent approvals)\n * stand down through `isHandledElsewhere`; viewers without edit rights see\n * `readOnlyNotice`.\n */\n\n/** The pending request a host slot renders or judges. */\nexport interface StatewireApprovalRequestContext {\n /** The parked input request — `undefined` while the run still streams (a live lane approval). */\n request: StatewireThread.CustomInputRequest | undefined;\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\n/** The request a host editor renders, plus the decisions it may send for it. */\nexport interface StatewireApprovalEditorContext extends StatewireApprovalRequestContext {\n /** A decision is in flight (or a live lane already answered); the editor must not send another. */\n locked: boolean;\n /** Whether \"approve all for this session\" may be offered for this batch. */\n sessionApprovalOffered: boolean;\n approve: () => void;\n approveForSession: () => void;\n reject: (reason?: string) => void;\n /**\n * Answer each gated call on its own — the only way an EDITED call reaches\n * the backend. `decision` is asked once per call, in order, so the answer\n * always names exactly the parked calls (a partial map is refused\n * server-side and nothing runs). `null` when the batch cannot take\n * per-action answers: a call without a `tool_call_id`, or a live / multi-\n * lane approval, which resumes per lane and carries no edit.\n */\n decideEach: ((decision: (request: PendingActionRequest) => HitlActionDecision) => void) | null;\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 * Replace BOTH the details region and the decision row with a host\n * composer that decides the request itself through the context's\n * callbacks (an editable email whose edits must be what runs). Return\n * `null`/`undefined` to keep the default card for this request. Not\n * consulted for read-only viewers or non-HITL parks.\n */\n renderEditor?: (context: StatewireApprovalEditorContext) => 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 renderEditor,\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 {\n request,\n requestId,\n hitl: parkedHitl,\n actionRequests: parkedActionRequests,\n liveHitl,\n liveActionRequests,\n queuedLaneDecisions,\n abandonedInterrupt,\n pending,\n resume,\n sendLaneDecisions,\n } = useStatewireApprovalRequest<StatewireThread.InputResponse>({\n interruptPredicate,\n pendingResumeGraceMs,\n onResumeRejected,\n });\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 // The live lanes a decision was sent for (joined ids); the buttons stay\n // locked until the host echoes them in `queuedLaneDecisions` or refuses.\n const [liveSentKey, setLiveSentKey] = useState<string>();\n\n const isLive = request === undefined && liveHitl !== null;\n const hitl = isLive ? liveHitl : parkedHitl;\n const actionRequests = isLive ? liveActionRequests : parkedActionRequests;\n const laneIds = laneInterruptIds(actionRequests);\n const undecidedLaneIds = laneIds?.filter((id) => !(id in queuedLaneDecisions)) ?? null;\n // A lone parked lane (or an unstamped batch) answers with one positional\n // `run/input`; that is the only resume the backend binds per-action answers\n // to, so it is the only place an edited call can go.\n const positional = laneIds === null || (!isLive && laneIds.length <= 1);\n const liveKey = isLive ? (laneIds?.join('|') ?? null) : null;\n const viewKey = requestId ?? liveKey ?? undefined;\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 prevViewKeyRef = useRef(viewKey);\n if (prevViewKeyRef.current !== viewKey) {\n prevViewKeyRef.current = viewKey;\n if (detailsOverride !== undefined) setDetailsOverride(undefined);\n if (liveSentKey !== undefined) setLiveSentKey(undefined);\n }\n\n const decide = useCallback(\n (value: Record<string, string>) => {\n // Several parked lanes (or a live one) answer by interrupt id: a\n // positional `run/input` would resume only the first and re-park the\n // rest. A lone parked lane keeps the positional resume every host\n // already speaks.\n if (positional || undecidedLaneIds === null) {\n resume(value);\n return;\n }\n const lanes = fanLaneDecision({ interruptIds: undecidedLaneIds, decision: value });\n if (!isLive) {\n sendLaneDecisions(lanes);\n return;\n }\n const key = undecidedLaneIds.join('|');\n setLiveSentKey(key);\n Promise.resolve(sendLaneDecisions(lanes)).catch((error: unknown) => {\n setLiveSentKey((current) => (current === key ? undefined : current));\n onResumeRejected?.(error, undecidedLaneIds[0] ?? '');\n });\n },\n [positional, undecidedLaneIds, isLive, resume, sendLaneDecisions, onResumeRejected],\n );\n\n const decideEach = useMemo<StatewireApprovalEditorContext['decideEach']>(() => {\n if (actionRequestToolCallIds(actionRequests) === null || !positional) return null;\n return (decision) => {\n const decisions: Record<string, HitlActionDecision> = {};\n for (const request of actionRequests) {\n if (request.toolCallId !== undefined) decisions[request.toolCallId] = decision(request);\n }\n // The edited argument set comes from the wire as JSON; the resume value\n // is that JSON going back.\n resume(hitlResumeActionDecisions(decisions) as StatewireThread.InputResponse);\n };\n }, [actionRequests, positional, resume]);\n\n if (!request && !isLive) {\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 // A live mirror without lane stamps cannot be answered before the run\n // parks; the parked request renders it then.\n if (isLive && laneIds === null) return null;\n\n const message = readInterruptMessage(request ? request.payload : liveHitl, 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 const sessionApprovalOffered = hitl !== null && canApproveForSession(actionRequests);\n const laneDecided =\n undecidedLaneIds !== null &&\n laneIds !== null &&\n (undecidedLaneIds.length === 0 || liveSentKey === undecidedLaneIds.join('|'));\n const locked = pending || laneDecided;\n\n const editor =\n !readOnly && hitl !== null && renderEditor\n ? renderEditor({\n ...context,\n locked,\n sessionApprovalOffered,\n approve: () => decide(hitlResumeApprove()),\n approveForSession: () => decide(hitlResumeApproveForSession()),\n reject: (reason) => decide(hitlResumeReject(reason)),\n decideEach,\n })\n : undefined;\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 {isLive ? (\n <p className=\"pt-1 text-amber-700 text-xs\" data-testid=\"statewire-approval-live\">\n {laneDecided\n ? 'Decision sent — it applies as soon as this step pauses.'\n : 'Other work keeps running while you decide.'}\n </p>\n ) : null}\n {hasDetails && editor == null ? (\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 — unless the host takes over the\n decision too (`renderEditor`), which sends the edit per call. */}\n {editor != null ? (\n <div className={cn('mt-3 flex flex-col gap-2', classNames?.details)}>{editor}</div>\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 {editor != null ? null : (\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={locked}\n onClick={() => decide(hitlResumeApprove())}\n className=\"bg-green-600 text-white hover:bg-green-700\"\n >\n <CheckIcon />\n Approve\n </Button>\n {sessionApprovalOffered ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={locked}\n onClick={() => decide(hitlResumeApproveForSession())}\n data-testid=\"statewire-approval-approve-session\"\n >\n <CheckCheckIcon />\n Approve all for this session\n </Button>\n ) : null}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={locked}\n onClick={() => decide(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 )}\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","laneInterruptIds","useRef","useCallback","fanLaneDecision","useMemo","actionRequestToolCallIds","request","hitlResumeActionDecisions","jsxs","cn","XIcon","readInterruptMessage","canApproveForSession","hitlResumeApprove","hitlResumeApproveForSession","hitlResumeReject","TriangleAlertIcon","Fragment","ChevronDownIcon","Button","CheckIcon","CheckCheckIcon","interruptResumeDecline"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StatewireThread } from '@assistant-ui/react-statewire';
|
|
2
|
-
import { type HitlApprovalPayload, type PendingActionRequest } from '@athenaintel/agent-runtime';
|
|
2
|
+
import { type HitlActionDecision, type HitlApprovalPayload, type PendingActionRequest } from '@athenaintel/agent-runtime';
|
|
3
3
|
import { type FC, type ReactNode } from 'react';
|
|
4
4
|
import { type StatewireInterruptPredicate } from './use-statewire-approval-request';
|
|
5
5
|
/**
|
|
@@ -26,9 +26,12 @@ import { type StatewireInterruptPredicate } from './use-statewire-approval-reque
|
|
|
26
26
|
* first of several parked lanes.
|
|
27
27
|
*
|
|
28
28
|
* Host-specific review UIs (subagent cards, email summaries) slot in through
|
|
29
|
-
* `renderDetails`;
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* `renderDetails`; a host composer that must also OWN the decision (an
|
|
30
|
+
* editable email whose edits have to reach the backend) takes over the
|
|
31
|
+
* details and the decision row through `renderEditor`; requests another
|
|
32
|
+
* surface answers (typed interrupt families, per-lane subagent approvals)
|
|
33
|
+
* stand down through `isHandledElsewhere`; viewers without edit rights see
|
|
34
|
+
* `readOnlyNotice`.
|
|
32
35
|
*/
|
|
33
36
|
/** The pending request a host slot renders or judges. */
|
|
34
37
|
export interface StatewireApprovalRequestContext {
|
|
@@ -39,6 +42,25 @@ export interface StatewireApprovalRequestContext {
|
|
|
39
42
|
/** Every gated call the approval covers, in decision order (empty for non-HITL parks). */
|
|
40
43
|
actionRequests: PendingActionRequest[];
|
|
41
44
|
}
|
|
45
|
+
/** The request a host editor renders, plus the decisions it may send for it. */
|
|
46
|
+
export interface StatewireApprovalEditorContext extends StatewireApprovalRequestContext {
|
|
47
|
+
/** A decision is in flight (or a live lane already answered); the editor must not send another. */
|
|
48
|
+
locked: boolean;
|
|
49
|
+
/** Whether "approve all for this session" may be offered for this batch. */
|
|
50
|
+
sessionApprovalOffered: boolean;
|
|
51
|
+
approve: () => void;
|
|
52
|
+
approveForSession: () => void;
|
|
53
|
+
reject: (reason?: string) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Answer each gated call on its own — the only way an EDITED call reaches
|
|
56
|
+
* the backend. `decision` is asked once per call, in order, so the answer
|
|
57
|
+
* always names exactly the parked calls (a partial map is refused
|
|
58
|
+
* server-side and nothing runs). `null` when the batch cannot take
|
|
59
|
+
* per-action answers: a call without a `tool_call_id`, or a live / multi-
|
|
60
|
+
* lane approval, which resumes per lane and carries no edit.
|
|
61
|
+
*/
|
|
62
|
+
decideEach: ((decision: (request: PendingActionRequest) => HitlActionDecision) => void) | null;
|
|
63
|
+
}
|
|
42
64
|
export interface StatewireApprovalCardProps {
|
|
43
65
|
/** Surface-styling overrides merged onto the default classes. */
|
|
44
66
|
classNames?: {
|
|
@@ -77,6 +99,14 @@ export interface StatewireApprovalCardProps {
|
|
|
77
99
|
* region; the decision buttons stay with the card.
|
|
78
100
|
*/
|
|
79
101
|
renderDetails?: (context: StatewireApprovalRequestContext) => ReactNode;
|
|
102
|
+
/**
|
|
103
|
+
* Replace BOTH the details region and the decision row with a host
|
|
104
|
+
* composer that decides the request itself through the context's
|
|
105
|
+
* callbacks (an editable email whose edits must be what runs). Return
|
|
106
|
+
* `null`/`undefined` to keep the default card for this request. Not
|
|
107
|
+
* consulted for read-only viewers or non-HITL parks.
|
|
108
|
+
*/
|
|
109
|
+
renderEditor?: (context: StatewireApprovalEditorContext) => ReactNode;
|
|
80
110
|
/**
|
|
81
111
|
* The viewer may see the request (it explains why the run is parked) but
|
|
82
112
|
* cannot decide it — answering steers the shared run. Renders
|
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useAuiState } from "@assistant-ui/react";
|
|
3
|
-
import "../packages/agent-runtime/src/generated/runconfig-contract.js";
|
|
4
|
-
import { useState, useRef, useCallback } from "react";
|
|
5
|
-
import "@assistant-ui/tap";
|
|
6
|
-
import "statewire";
|
|
7
|
-
import "../packages/agent-runtime/src/statewire-suspended.js";
|
|
8
|
-
import "../packages/agent-runtime/src/conversation-list.js";
|
|
9
|
-
import "@tanstack/react-query";
|
|
10
|
-
import { laneInterruptIds, fanLaneDecision, readInterruptMessage, canApproveForSession, hitlResumeApprove, hitlResumeApproveForSession, hitlResumeReject, interruptResumeDecline } from "../packages/agent-runtime/src/interrupts.js";
|
|
11
|
-
import "../packages/agent-runtime/src/messages.js";
|
|
12
|
-
import "../packages/agent-runtime/src/statewire-state.js";
|
|
13
|
-
import { hasStatewireThreadExtras } from "../packages/agent-runtime/src/thread-extras.js";
|
|
14
|
-
import "@assistant-ui/store";
|
|
15
|
-
import "../packages/agent-runtime/src/quota-safe-local-storage-session.js";
|
|
16
|
-
import "../packages/agent-runtime/src/statewire-attach-observer.js";
|
|
17
|
-
import "../packages/agent-runtime/src/thread-status.js";
|
|
18
|
-
import "@assistant-ui/react-statewire";
|
|
19
|
-
import "@assistant-ui/core";
|
|
20
|
-
import "../packages/agent-runtime/src/thread-handoff.js";
|
|
21
3
|
import { XIcon, TriangleAlertIcon, ChevronDownIcon, CheckIcon, CheckCheckIcon } from "lucide-react";
|
|
4
|
+
import { useState, useRef, useCallback, useMemo } from "react";
|
|
22
5
|
import { Button } from "../ui/button.js";
|
|
23
6
|
import { useStatewireApprovalRequest } from "./use-statewire-approval-request.js";
|
|
7
|
+
import { hasStatewireThreadExtras } from "../packages/agent-runtime/src/thread-extras.js";
|
|
8
|
+
import { laneInterruptIds, fanLaneDecision, actionRequestToolCallIds, hitlResumeActionDecisions, readInterruptMessage, canApproveForSession, hitlResumeReject, hitlResumeApproveForSession, hitlResumeApprove, interruptResumeDecline } from "../packages/agent-runtime/src/interrupts.js";
|
|
24
9
|
import { cn } from "cnfast";
|
|
25
10
|
const StatewireApprovalCard = (props) => {
|
|
26
11
|
const extras = useAuiState((s) => s.thread.extras);
|
|
@@ -34,6 +19,7 @@ const StatewireApprovalCardInner = ({
|
|
|
34
19
|
pendingResumeGraceMs,
|
|
35
20
|
isHandledElsewhere,
|
|
36
21
|
renderDetails,
|
|
22
|
+
renderEditor,
|
|
37
23
|
readOnly = false,
|
|
38
24
|
readOnlyNotice = "Waiting for someone with edit access to respond.",
|
|
39
25
|
nonHitlNotice = "This request needs an answer this surface cannot collect.",
|
|
@@ -52,7 +38,11 @@ const StatewireApprovalCardInner = ({
|
|
|
52
38
|
pending,
|
|
53
39
|
resume,
|
|
54
40
|
sendLaneDecisions
|
|
55
|
-
} = useStatewireApprovalRequest({
|
|
41
|
+
} = useStatewireApprovalRequest({
|
|
42
|
+
interruptPredicate,
|
|
43
|
+
pendingResumeGraceMs,
|
|
44
|
+
onResumeRejected
|
|
45
|
+
});
|
|
56
46
|
const [dismissedId, setDismissedId] = useState();
|
|
57
47
|
const [detailsOverride, setDetailsOverride] = useState();
|
|
58
48
|
const [liveSentKey, setLiveSentKey] = useState();
|
|
@@ -61,6 +51,7 @@ const StatewireApprovalCardInner = ({
|
|
|
61
51
|
const actionRequests = isLive ? liveActionRequests : parkedActionRequests;
|
|
62
52
|
const laneIds = laneInterruptIds(actionRequests);
|
|
63
53
|
const undecidedLaneIds = (laneIds == null ? void 0 : laneIds.filter((id) => !(id in queuedLaneDecisions))) ?? null;
|
|
54
|
+
const positional = laneIds === null || !isLive && laneIds.length <= 1;
|
|
64
55
|
const liveKey = isLive ? (laneIds == null ? void 0 : laneIds.join("|")) ?? null : null;
|
|
65
56
|
const viewKey = requestId ?? liveKey ?? void 0;
|
|
66
57
|
const prevViewKeyRef = useRef(viewKey);
|
|
@@ -71,7 +62,7 @@ const StatewireApprovalCardInner = ({
|
|
|
71
62
|
}
|
|
72
63
|
const decide = useCallback(
|
|
73
64
|
(value) => {
|
|
74
|
-
if (
|
|
65
|
+
if (positional || undecidedLaneIds === null) {
|
|
75
66
|
resume(value);
|
|
76
67
|
return;
|
|
77
68
|
}
|
|
@@ -87,8 +78,18 @@ const StatewireApprovalCardInner = ({
|
|
|
87
78
|
onResumeRejected == null ? void 0 : onResumeRejected(error, undecidedLaneIds[0] ?? "");
|
|
88
79
|
});
|
|
89
80
|
},
|
|
90
|
-
[
|
|
81
|
+
[positional, undecidedLaneIds, isLive, resume, sendLaneDecisions, onResumeRejected]
|
|
91
82
|
);
|
|
83
|
+
const decideEach = useMemo(() => {
|
|
84
|
+
if (actionRequestToolCallIds(actionRequests) === null || !positional) return null;
|
|
85
|
+
return (decision) => {
|
|
86
|
+
const decisions = {};
|
|
87
|
+
for (const request2 of actionRequests) {
|
|
88
|
+
if (request2.toolCallId !== void 0) decisions[request2.toolCallId] = decision(request2);
|
|
89
|
+
}
|
|
90
|
+
resume(hitlResumeActionDecisions(decisions));
|
|
91
|
+
};
|
|
92
|
+
}, [actionRequests, positional, resume]);
|
|
92
93
|
if (!request && !isLive) {
|
|
93
94
|
if (!abandonedInterrupt || abandonedInterrupt.id === dismissedId) return null;
|
|
94
95
|
return /* @__PURE__ */ jsxs(
|
|
@@ -125,6 +126,15 @@ const StatewireApprovalCardInner = ({
|
|
|
125
126
|
const sessionApprovalOffered = hitl !== null && canApproveForSession(actionRequests);
|
|
126
127
|
const laneDecided = undecidedLaneIds !== null && laneIds !== null && (undecidedLaneIds.length === 0 || liveSentKey === undecidedLaneIds.join("|"));
|
|
127
128
|
const locked = pending || laneDecided;
|
|
129
|
+
const editor = !readOnly && hitl !== null && renderEditor ? renderEditor({
|
|
130
|
+
...context,
|
|
131
|
+
locked,
|
|
132
|
+
sessionApprovalOffered,
|
|
133
|
+
approve: () => decide(hitlResumeApprove()),
|
|
134
|
+
approveForSession: () => decide(hitlResumeApproveForSession()),
|
|
135
|
+
reject: (reason) => decide(hitlResumeReject(reason)),
|
|
136
|
+
decideEach
|
|
137
|
+
}) : void 0;
|
|
128
138
|
return /* @__PURE__ */ jsxs(
|
|
129
139
|
"section",
|
|
130
140
|
{
|
|
@@ -149,7 +159,7 @@ const StatewireApprovalCardInner = ({
|
|
|
149
159
|
" requires approval before it runs."
|
|
150
160
|
] }) : message }),
|
|
151
161
|
isLive ? /* @__PURE__ */ jsx("p", { className: "pt-1 text-amber-700 text-xs", "data-testid": "statewire-approval-live", children: laneDecided ? "Decision sent — it applies as soon as this step pauses." : "Other work keeps running while you decide." }) : null,
|
|
152
|
-
hasDetails ? /* @__PURE__ */ jsxs(
|
|
162
|
+
hasDetails && editor == null ? /* @__PURE__ */ jsxs(
|
|
153
163
|
"button",
|
|
154
164
|
{
|
|
155
165
|
type: "button",
|
|
@@ -167,10 +177,10 @@ const StatewireApprovalCardInner = ({
|
|
|
167
177
|
]
|
|
168
178
|
}
|
|
169
179
|
) : null,
|
|
170
|
-
showDetails ? /* @__PURE__ */ jsx("div", { className: cn("mt-2 flex flex-col gap-2", classNames == null ? void 0 : classNames.details), children: renderDetails ? renderDetails(context) : /* @__PURE__ */ jsx(DefaultApprovalDetails, { actionRequests }) }) : null
|
|
180
|
+
editor != null ? /* @__PURE__ */ jsx("div", { className: cn("mt-3 flex flex-col gap-2", classNames == null ? void 0 : classNames.details), children: editor }) : showDetails ? /* @__PURE__ */ jsx("div", { className: cn("mt-2 flex flex-col gap-2", classNames == null ? void 0 : classNames.details), children: renderDetails ? renderDetails(context) : /* @__PURE__ */ jsx(DefaultApprovalDetails, { actionRequests }) }) : null
|
|
171
181
|
] })
|
|
172
182
|
] }),
|
|
173
|
-
/* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap items-center gap-2 pt-3", classNames == null ? void 0 : classNames.actions), children: readOnly ? /* @__PURE__ */ jsx("p", { className: "text-amber-800 text-sm", "data-testid": "statewire-approval-read-only", children: readOnlyNotice }) : hitl ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
183
|
+
editor != null ? null : /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap items-center gap-2 pt-3", classNames == null ? void 0 : classNames.actions), children: readOnly ? /* @__PURE__ */ jsx("p", { className: "text-amber-800 text-sm", "data-testid": "statewire-approval-read-only", children: readOnlyNotice }) : hitl ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
174
184
|
/* @__PURE__ */ jsxs(
|
|
175
185
|
Button,
|
|
176
186
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatewireApprovalCard.js","sources":["../../src/chat/StatewireApprovalCard.tsx"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport {\n canApproveForSession,\n fanLaneDecision,\n hasStatewireThreadExtras,\n type HitlApprovalPayload,\n hitlResumeApprove,\n hitlResumeApproveForSession,\n hitlResumeReject,\n interruptResumeDecline,\n laneInterruptIds,\n type PendingActionRequest,\n readInterruptMessage,\n} from '@athenaintel/agent-runtime';\nimport {\n CheckCheckIcon,\n CheckIcon,\n ChevronDownIcon,\n TriangleAlertIcon,\n XIcon,\n} from 'lucide-react';\nimport { type FC, type ReactNode, useCallback, 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 * A gated call raised inside a delegated subagent parks only its lane: the\n * host mirrors that interrupt while the sibling lanes keep streaming, before\n * any input request exists. The card renders the mirrored payload the same\n * way and answers it with `run/lane-input` keyed by the lane's langgraph\n * interrupt id (`laneInterruptIds`); the host queues the decision and applies\n * it the moment the run parks, so the approver never waits on the siblings.\n * Once parked, a payload whose calls all carry interrupt ids resumes as a\n * `lane_decisions` map too — one positional `run/input` can only answer the\n * first of several parked lanes.\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 /** The parked input request — `undefined` while the run still streams (a live lane approval). */\n request: StatewireThread.CustomInputRequest | undefined;\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 {\n request,\n requestId,\n hitl: parkedHitl,\n actionRequests: parkedActionRequests,\n liveHitl,\n liveActionRequests,\n queuedLaneDecisions,\n abandonedInterrupt,\n pending,\n resume,\n sendLaneDecisions,\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 // The live lanes a decision was sent for (joined ids); the buttons stay\n // locked until the host echoes them in `queuedLaneDecisions` or refuses.\n const [liveSentKey, setLiveSentKey] = useState<string>();\n\n const isLive = request === undefined && liveHitl !== null;\n const hitl = isLive ? liveHitl : parkedHitl;\n const actionRequests = isLive ? liveActionRequests : parkedActionRequests;\n const laneIds = laneInterruptIds(actionRequests);\n const undecidedLaneIds = laneIds?.filter((id) => !(id in queuedLaneDecisions)) ?? null;\n const liveKey = isLive ? (laneIds?.join('|') ?? null) : null;\n const viewKey = requestId ?? liveKey ?? undefined;\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 prevViewKeyRef = useRef(viewKey);\n if (prevViewKeyRef.current !== viewKey) {\n prevViewKeyRef.current = viewKey;\n if (detailsOverride !== undefined) setDetailsOverride(undefined);\n if (liveSentKey !== undefined) setLiveSentKey(undefined);\n }\n\n const decide = useCallback(\n (value: Record<string, string>) => {\n // Several parked lanes (or a live one) answer by interrupt id: a\n // positional `run/input` would resume only the first and re-park the\n // rest. A lone parked lane keeps the positional resume every host\n // already speaks.\n if (laneIds === null || undecidedLaneIds === null || (!isLive && laneIds.length <= 1)) {\n resume(value);\n return;\n }\n const lanes = fanLaneDecision({ interruptIds: undecidedLaneIds, decision: value });\n if (!isLive) {\n sendLaneDecisions(lanes);\n return;\n }\n const key = undecidedLaneIds.join('|');\n setLiveSentKey(key);\n Promise.resolve(sendLaneDecisions(lanes)).catch((error: unknown) => {\n setLiveSentKey((current) => (current === key ? undefined : current));\n onResumeRejected?.(error, undecidedLaneIds[0] ?? '');\n });\n },\n [laneIds, undecidedLaneIds, isLive, resume, sendLaneDecisions, onResumeRejected],\n );\n\n if (!request && !isLive) {\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 // A live mirror without lane stamps cannot be answered before the run\n // parks; the parked request renders it then.\n if (isLive && laneIds === null) return null;\n\n const message = readInterruptMessage(request ? request.payload : liveHitl, 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 const sessionApprovalOffered = hitl !== null && canApproveForSession(actionRequests);\n const laneDecided =\n undecidedLaneIds !== null &&\n laneIds !== null &&\n (undecidedLaneIds.length === 0 || liveSentKey === undecidedLaneIds.join('|'));\n const locked = pending || laneDecided;\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 {isLive ? (\n <p className=\"pt-1 text-amber-700 text-xs\" data-testid=\"statewire-approval-live\">\n {laneDecided\n ? 'Decision sent — it applies as soon as this step pauses.'\n : 'Other work keeps running while you decide.'}\n </p>\n ) : null}\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={locked}\n onClick={() => decide(hitlResumeApprove())}\n className=\"bg-green-600 text-white hover:bg-green-700\"\n >\n <CheckIcon />\n Approve\n </Button>\n {sessionApprovalOffered ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={locked}\n onClick={() => decide(hitlResumeApproveForSession())}\n data-testid=\"statewire-approval-approve-session\"\n >\n <CheckCheckIcon />\n Approve all for this session\n </Button>\n ) : null}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={locked}\n onClick={() => decide(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":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StatewireApprovalCard.js","sources":["../../src/chat/StatewireApprovalCard.tsx"],"sourcesContent":["import { useAuiState } from '@assistant-ui/react';\nimport type { StatewireThread } from '@assistant-ui/react-statewire';\nimport {\n actionRequestToolCallIds,\n canApproveForSession,\n fanLaneDecision,\n hasStatewireThreadExtras,\n type HitlActionDecision,\n type HitlApprovalPayload,\n hitlResumeActionDecisions,\n hitlResumeApprove,\n hitlResumeApproveForSession,\n hitlResumeReject,\n interruptResumeDecline,\n laneInterruptIds,\n type PendingActionRequest,\n readInterruptMessage,\n} from '@athenaintel/agent-runtime';\nimport {\n CheckCheckIcon,\n CheckIcon,\n ChevronDownIcon,\n TriangleAlertIcon,\n XIcon,\n} from 'lucide-react';\nimport { type FC, type ReactNode, useCallback, useMemo, 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 * A gated call raised inside a delegated subagent parks only its lane: the\n * host mirrors that interrupt while the sibling lanes keep streaming, before\n * any input request exists. The card renders the mirrored payload the same\n * way and answers it with `run/lane-input` keyed by the lane's langgraph\n * interrupt id (`laneInterruptIds`); the host queues the decision and applies\n * it the moment the run parks, so the approver never waits on the siblings.\n * Once parked, a payload whose calls all carry interrupt ids resumes as a\n * `lane_decisions` map too — one positional `run/input` can only answer the\n * first of several parked lanes.\n *\n * Host-specific review UIs (subagent cards, email summaries) slot in through\n * `renderDetails`; a host composer that must also OWN the decision (an\n * editable email whose edits have to reach the backend) takes over the\n * details and the decision row through `renderEditor`; requests another\n * surface answers (typed interrupt families, per-lane subagent approvals)\n * stand down through `isHandledElsewhere`; viewers without edit rights see\n * `readOnlyNotice`.\n */\n\n/** The pending request a host slot renders or judges. */\nexport interface StatewireApprovalRequestContext {\n /** The parked input request — `undefined` while the run still streams (a live lane approval). */\n request: StatewireThread.CustomInputRequest | undefined;\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\n/** The request a host editor renders, plus the decisions it may send for it. */\nexport interface StatewireApprovalEditorContext extends StatewireApprovalRequestContext {\n /** A decision is in flight (or a live lane already answered); the editor must not send another. */\n locked: boolean;\n /** Whether \"approve all for this session\" may be offered for this batch. */\n sessionApprovalOffered: boolean;\n approve: () => void;\n approveForSession: () => void;\n reject: (reason?: string) => void;\n /**\n * Answer each gated call on its own — the only way an EDITED call reaches\n * the backend. `decision` is asked once per call, in order, so the answer\n * always names exactly the parked calls (a partial map is refused\n * server-side and nothing runs). `null` when the batch cannot take\n * per-action answers: a call without a `tool_call_id`, or a live / multi-\n * lane approval, which resumes per lane and carries no edit.\n */\n decideEach: ((decision: (request: PendingActionRequest) => HitlActionDecision) => void) | null;\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 * Replace BOTH the details region and the decision row with a host\n * composer that decides the request itself through the context's\n * callbacks (an editable email whose edits must be what runs). Return\n * `null`/`undefined` to keep the default card for this request. Not\n * consulted for read-only viewers or non-HITL parks.\n */\n renderEditor?: (context: StatewireApprovalEditorContext) => 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 renderEditor,\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 {\n request,\n requestId,\n hitl: parkedHitl,\n actionRequests: parkedActionRequests,\n liveHitl,\n liveActionRequests,\n queuedLaneDecisions,\n abandonedInterrupt,\n pending,\n resume,\n sendLaneDecisions,\n } = useStatewireApprovalRequest<StatewireThread.InputResponse>({\n interruptPredicate,\n pendingResumeGraceMs,\n onResumeRejected,\n });\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 // The live lanes a decision was sent for (joined ids); the buttons stay\n // locked until the host echoes them in `queuedLaneDecisions` or refuses.\n const [liveSentKey, setLiveSentKey] = useState<string>();\n\n const isLive = request === undefined && liveHitl !== null;\n const hitl = isLive ? liveHitl : parkedHitl;\n const actionRequests = isLive ? liveActionRequests : parkedActionRequests;\n const laneIds = laneInterruptIds(actionRequests);\n const undecidedLaneIds = laneIds?.filter((id) => !(id in queuedLaneDecisions)) ?? null;\n // A lone parked lane (or an unstamped batch) answers with one positional\n // `run/input`; that is the only resume the backend binds per-action answers\n // to, so it is the only place an edited call can go.\n const positional = laneIds === null || (!isLive && laneIds.length <= 1);\n const liveKey = isLive ? (laneIds?.join('|') ?? null) : null;\n const viewKey = requestId ?? liveKey ?? undefined;\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 prevViewKeyRef = useRef(viewKey);\n if (prevViewKeyRef.current !== viewKey) {\n prevViewKeyRef.current = viewKey;\n if (detailsOverride !== undefined) setDetailsOverride(undefined);\n if (liveSentKey !== undefined) setLiveSentKey(undefined);\n }\n\n const decide = useCallback(\n (value: Record<string, string>) => {\n // Several parked lanes (or a live one) answer by interrupt id: a\n // positional `run/input` would resume only the first and re-park the\n // rest. A lone parked lane keeps the positional resume every host\n // already speaks.\n if (positional || undecidedLaneIds === null) {\n resume(value);\n return;\n }\n const lanes = fanLaneDecision({ interruptIds: undecidedLaneIds, decision: value });\n if (!isLive) {\n sendLaneDecisions(lanes);\n return;\n }\n const key = undecidedLaneIds.join('|');\n setLiveSentKey(key);\n Promise.resolve(sendLaneDecisions(lanes)).catch((error: unknown) => {\n setLiveSentKey((current) => (current === key ? undefined : current));\n onResumeRejected?.(error, undecidedLaneIds[0] ?? '');\n });\n },\n [positional, undecidedLaneIds, isLive, resume, sendLaneDecisions, onResumeRejected],\n );\n\n const decideEach = useMemo<StatewireApprovalEditorContext['decideEach']>(() => {\n if (actionRequestToolCallIds(actionRequests) === null || !positional) return null;\n return (decision) => {\n const decisions: Record<string, HitlActionDecision> = {};\n for (const request of actionRequests) {\n if (request.toolCallId !== undefined) decisions[request.toolCallId] = decision(request);\n }\n // The edited argument set comes from the wire as JSON; the resume value\n // is that JSON going back.\n resume(hitlResumeActionDecisions(decisions) as StatewireThread.InputResponse);\n };\n }, [actionRequests, positional, resume]);\n\n if (!request && !isLive) {\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 // A live mirror without lane stamps cannot be answered before the run\n // parks; the parked request renders it then.\n if (isLive && laneIds === null) return null;\n\n const message = readInterruptMessage(request ? request.payload : liveHitl, 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 const sessionApprovalOffered = hitl !== null && canApproveForSession(actionRequests);\n const laneDecided =\n undecidedLaneIds !== null &&\n laneIds !== null &&\n (undecidedLaneIds.length === 0 || liveSentKey === undecidedLaneIds.join('|'));\n const locked = pending || laneDecided;\n\n const editor =\n !readOnly && hitl !== null && renderEditor\n ? renderEditor({\n ...context,\n locked,\n sessionApprovalOffered,\n approve: () => decide(hitlResumeApprove()),\n approveForSession: () => decide(hitlResumeApproveForSession()),\n reject: (reason) => decide(hitlResumeReject(reason)),\n decideEach,\n })\n : undefined;\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 {isLive ? (\n <p className=\"pt-1 text-amber-700 text-xs\" data-testid=\"statewire-approval-live\">\n {laneDecided\n ? 'Decision sent — it applies as soon as this step pauses.'\n : 'Other work keeps running while you decide.'}\n </p>\n ) : null}\n {hasDetails && editor == null ? (\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 — unless the host takes over the\n decision too (`renderEditor`), which sends the edit per call. */}\n {editor != null ? (\n <div className={cn('mt-3 flex flex-col gap-2', classNames?.details)}>{editor}</div>\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 {editor != null ? null : (\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={locked}\n onClick={() => decide(hitlResumeApprove())}\n className=\"bg-green-600 text-white hover:bg-green-700\"\n >\n <CheckIcon />\n Approve\n </Button>\n {sessionApprovalOffered ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={locked}\n onClick={() => decide(hitlResumeApproveForSession())}\n data-testid=\"statewire-approval-approve-session\"\n >\n <CheckCheckIcon />\n Approve all for this session\n </Button>\n ) : null}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={locked}\n onClick={() => decide(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 )}\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":["request"],"mappings":"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"}
|
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const reactStatewire = require("@assistant-ui/react-statewire");
|
|
4
|
-
const clientTools = require("../packages/agent-runtime/src/client-tools.cjs");
|
|
5
|
-
const clientToolBridge = require("../packages/agent-runtime/src/client-tool-bridge.cjs");
|
|
6
|
-
require("@assistant-ui/tap");
|
|
7
4
|
const react = require("react");
|
|
8
|
-
require("
|
|
9
|
-
require("../packages/agent-runtime/src/
|
|
10
|
-
require("../packages/agent-runtime/src/conversation-list.cjs");
|
|
11
|
-
require("@tanstack/react-query");
|
|
12
|
-
require("../packages/agent-runtime/src/generated/runconfig-contract.cjs");
|
|
13
|
-
require("../packages/agent-runtime/src/statewire-state.cjs");
|
|
14
|
-
require("../packages/agent-runtime/src/messages.cjs");
|
|
15
|
-
require("@assistant-ui/store");
|
|
16
|
-
require("../packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
|
|
17
|
-
require("../packages/agent-runtime/src/statewire-attach-observer.cjs");
|
|
18
|
-
require("../packages/agent-runtime/src/thread-status.cjs");
|
|
19
|
-
require("@assistant-ui/core");
|
|
20
|
-
require("../packages/agent-runtime/src/thread-handoff.cjs");
|
|
5
|
+
const clientToolBridge = require("../packages/agent-runtime/src/client-tool-bridge.cjs");
|
|
6
|
+
const clientTools = require("../packages/agent-runtime/src/client-tools.cjs");
|
|
21
7
|
const clientToolRequests = clientTools.createClientToolRequestTracker();
|
|
22
8
|
function StatewireClientToolBridge({
|
|
23
9
|
tools,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatewireClientToolBridge.cjs","sources":["../../src/chat/StatewireClientToolBridge.tsx"],"sourcesContent":["import { useStatewireRuns, useStatewireSendCommand } from '@assistant-ui/react-statewire';\nimport {\n type ClientToolBridgeTool,\n createClientToolRequestTracker,\n type RegisteredClientTool,\n useStatewireClientToolBridge,\n} from '@athenaintel/agent-runtime';\nimport type { ReadonlyJSONObject } from 'assistant-stream/utils';\nimport { useMemo } from 'react';\n\n// Shared across every mount: the same thread can be attached from two mounted\n// surfaces, and a parked client-tool call must be executed and resumed by\n// exactly one of them. Whichever bridge claims the request first owns it.\nconst clientToolRequests = createClientToolRequestTracker();\n\n/**\n * Executes the deep agent's frontend tool calls in statewire mode.\n *\n * Tools declared through `runConfig.custom.client_tools` have no backend\n * implementation: a call parks the run and surfaces here as a statewire\n * input request. The bridge runs the matching local handler and resumes the\n * run with a per-call result envelope, so a missing tool or a thrown handler\n * still frees the run with an error the model can act on instead of hanging.\n *\n * The claim lifecycle, liveness guards, and wire-safety live in\n * `@athenaintel/agent-runtime`'s `useStatewireClientToolBridge`; this wrapper\n * only adapts the shared `RegisteredClientTool` shape and the SDK's transport.\n */\nexport function StatewireClientToolBridge({\n tools,\n threadId,\n}: {\n tools: readonly RegisteredClientTool[];\n threadId: string;\n}): null {\n const sendCommand = useStatewireSendCommand();\n const inputRequests = useStatewireRuns().inputRequests;\n\n const bridgeTools = useMemo<readonly ClientToolBridgeTool[]>(\n () =>\n tools.map((tool) => ({\n name: tool.definition.name,\n requiresWrite: tool.definition.requiresWrite,\n run: (args, context) => tool.handler(args, context),\n })),\n [tools],\n );\n\n useStatewireClientToolBridge({\n threadId,\n tools: bridgeTools,\n tracker: clientToolRequests,\n inputRequests,\n sendResume: (requestId, value) => {\n // Every envelope value is already wire-safe (per-call JSON round-trip in\n // the shared batch engine), so the batch only needs the transport's\n // JSON typing.\n return sendCommand({\n type: 'run/input',\n requestId,\n response: { type: 'resume', value: value as ReadonlyJSONObject },\n });\n },\n });\n\n return null;\n}\n"],"names":["createClientToolRequestTracker","useStatewireSendCommand","useStatewireRuns","useMemo","useStatewireClientToolBridge"],"mappings":"
|
|
1
|
+
{"version":3,"file":"StatewireClientToolBridge.cjs","sources":["../../src/chat/StatewireClientToolBridge.tsx"],"sourcesContent":["import { useStatewireRuns, useStatewireSendCommand } from '@assistant-ui/react-statewire';\nimport {\n type ClientToolBridgeTool,\n createClientToolRequestTracker,\n type RegisteredClientTool,\n useStatewireClientToolBridge,\n} from '@athenaintel/agent-runtime';\nimport type { ReadonlyJSONObject } from 'assistant-stream/utils';\nimport { useMemo } from 'react';\n\n// Shared across every mount: the same thread can be attached from two mounted\n// surfaces, and a parked client-tool call must be executed and resumed by\n// exactly one of them. Whichever bridge claims the request first owns it.\nconst clientToolRequests = createClientToolRequestTracker();\n\n/**\n * Executes the deep agent's frontend tool calls in statewire mode.\n *\n * Tools declared through `runConfig.custom.client_tools` have no backend\n * implementation: a call parks the run and surfaces here as a statewire\n * input request. The bridge runs the matching local handler and resumes the\n * run with a per-call result envelope, so a missing tool or a thrown handler\n * still frees the run with an error the model can act on instead of hanging.\n *\n * The claim lifecycle, liveness guards, and wire-safety live in\n * `@athenaintel/agent-runtime`'s `useStatewireClientToolBridge`; this wrapper\n * only adapts the shared `RegisteredClientTool` shape and the SDK's transport.\n */\nexport function StatewireClientToolBridge({\n tools,\n threadId,\n}: {\n tools: readonly RegisteredClientTool[];\n threadId: string;\n}): null {\n const sendCommand = useStatewireSendCommand();\n const inputRequests = useStatewireRuns().inputRequests;\n\n const bridgeTools = useMemo<readonly ClientToolBridgeTool[]>(\n () =>\n tools.map((tool) => ({\n name: tool.definition.name,\n requiresWrite: tool.definition.requiresWrite,\n run: (args, context) => tool.handler(args, context),\n })),\n [tools],\n );\n\n useStatewireClientToolBridge({\n threadId,\n tools: bridgeTools,\n tracker: clientToolRequests,\n inputRequests,\n sendResume: (requestId, value) => {\n // Every envelope value is already wire-safe (per-call JSON round-trip in\n // the shared batch engine), so the batch only needs the transport's\n // JSON typing.\n return sendCommand({\n type: 'run/input',\n requestId,\n response: { type: 'resume', value: value as ReadonlyJSONObject },\n });\n },\n });\n\n return null;\n}\n"],"names":["createClientToolRequestTracker","useStatewireSendCommand","useStatewireRuns","useMemo","useStatewireClientToolBridge"],"mappings":";;;;;;AAaA,MAAM,qBAAqBA,YAAAA,+BAAA;AAepB,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAGS;AACP,QAAM,cAAcC,eAAAA,wBAAA;AACpB,QAAM,gBAAgBC,eAAAA,mBAAmB;AAEzC,QAAM,cAAcC,MAAAA;AAAAA,IAClB,MACE,MAAM,IAAI,CAAC,UAAU;AAAA,MACnB,MAAM,KAAK,WAAW;AAAA,MACtB,eAAe,KAAK,WAAW;AAAA,MAC/B,KAAK,CAAC,MAAM,YAAY,KAAK,QAAQ,MAAM,OAAO;AAAA,IAAA,EAClD;AAAA,IACJ,CAAC,KAAK;AAAA,EAAA;AAGRC,gDAA6B;AAAA,IAC3B;AAAA,IACA,OAAO;AAAA,IACP,SAAS;AAAA,IACT;AAAA,IACA,YAAY,CAAC,WAAW,UAAU;AAIhC,aAAO,YAAY;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QACA,UAAU,EAAE,MAAM,UAAU,MAAA;AAAA,MAAmC,CAChE;AAAA,IACH;AAAA,EAAA,CACD;AAED,SAAO;AACT;;"}
|
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
import { useStatewireSendCommand, useStatewireRuns } from "@assistant-ui/react-statewire";
|
|
2
|
-
import { createClientToolRequestTracker } from "../packages/agent-runtime/src/client-tools.js";
|
|
3
|
-
import { useStatewireClientToolBridge } from "../packages/agent-runtime/src/client-tool-bridge.js";
|
|
4
|
-
import "@assistant-ui/tap";
|
|
5
2
|
import { useMemo } from "react";
|
|
6
|
-
import "
|
|
7
|
-
import "../packages/agent-runtime/src/
|
|
8
|
-
import "../packages/agent-runtime/src/conversation-list.js";
|
|
9
|
-
import "@tanstack/react-query";
|
|
10
|
-
import "../packages/agent-runtime/src/generated/runconfig-contract.js";
|
|
11
|
-
import "../packages/agent-runtime/src/statewire-state.js";
|
|
12
|
-
import "../packages/agent-runtime/src/messages.js";
|
|
13
|
-
import "@assistant-ui/store";
|
|
14
|
-
import "../packages/agent-runtime/src/quota-safe-local-storage-session.js";
|
|
15
|
-
import "../packages/agent-runtime/src/statewire-attach-observer.js";
|
|
16
|
-
import "../packages/agent-runtime/src/thread-status.js";
|
|
17
|
-
import "@assistant-ui/core";
|
|
18
|
-
import "../packages/agent-runtime/src/thread-handoff.js";
|
|
3
|
+
import { useStatewireClientToolBridge } from "../packages/agent-runtime/src/client-tool-bridge.js";
|
|
4
|
+
import { createClientToolRequestTracker } from "../packages/agent-runtime/src/client-tools.js";
|
|
19
5
|
const clientToolRequests = createClientToolRequestTracker();
|
|
20
6
|
function StatewireClientToolBridge({
|
|
21
7
|
tools,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatewireClientToolBridge.js","sources":["../../src/chat/StatewireClientToolBridge.tsx"],"sourcesContent":["import { useStatewireRuns, useStatewireSendCommand } from '@assistant-ui/react-statewire';\nimport {\n type ClientToolBridgeTool,\n createClientToolRequestTracker,\n type RegisteredClientTool,\n useStatewireClientToolBridge,\n} from '@athenaintel/agent-runtime';\nimport type { ReadonlyJSONObject } from 'assistant-stream/utils';\nimport { useMemo } from 'react';\n\n// Shared across every mount: the same thread can be attached from two mounted\n// surfaces, and a parked client-tool call must be executed and resumed by\n// exactly one of them. Whichever bridge claims the request first owns it.\nconst clientToolRequests = createClientToolRequestTracker();\n\n/**\n * Executes the deep agent's frontend tool calls in statewire mode.\n *\n * Tools declared through `runConfig.custom.client_tools` have no backend\n * implementation: a call parks the run and surfaces here as a statewire\n * input request. The bridge runs the matching local handler and resumes the\n * run with a per-call result envelope, so a missing tool or a thrown handler\n * still frees the run with an error the model can act on instead of hanging.\n *\n * The claim lifecycle, liveness guards, and wire-safety live in\n * `@athenaintel/agent-runtime`'s `useStatewireClientToolBridge`; this wrapper\n * only adapts the shared `RegisteredClientTool` shape and the SDK's transport.\n */\nexport function StatewireClientToolBridge({\n tools,\n threadId,\n}: {\n tools: readonly RegisteredClientTool[];\n threadId: string;\n}): null {\n const sendCommand = useStatewireSendCommand();\n const inputRequests = useStatewireRuns().inputRequests;\n\n const bridgeTools = useMemo<readonly ClientToolBridgeTool[]>(\n () =>\n tools.map((tool) => ({\n name: tool.definition.name,\n requiresWrite: tool.definition.requiresWrite,\n run: (args, context) => tool.handler(args, context),\n })),\n [tools],\n );\n\n useStatewireClientToolBridge({\n threadId,\n tools: bridgeTools,\n tracker: clientToolRequests,\n inputRequests,\n sendResume: (requestId, value) => {\n // Every envelope value is already wire-safe (per-call JSON round-trip in\n // the shared batch engine), so the batch only needs the transport's\n // JSON typing.\n return sendCommand({\n type: 'run/input',\n requestId,\n response: { type: 'resume', value: value as ReadonlyJSONObject },\n });\n },\n });\n\n return null;\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StatewireClientToolBridge.js","sources":["../../src/chat/StatewireClientToolBridge.tsx"],"sourcesContent":["import { useStatewireRuns, useStatewireSendCommand } from '@assistant-ui/react-statewire';\nimport {\n type ClientToolBridgeTool,\n createClientToolRequestTracker,\n type RegisteredClientTool,\n useStatewireClientToolBridge,\n} from '@athenaintel/agent-runtime';\nimport type { ReadonlyJSONObject } from 'assistant-stream/utils';\nimport { useMemo } from 'react';\n\n// Shared across every mount: the same thread can be attached from two mounted\n// surfaces, and a parked client-tool call must be executed and resumed by\n// exactly one of them. Whichever bridge claims the request first owns it.\nconst clientToolRequests = createClientToolRequestTracker();\n\n/**\n * Executes the deep agent's frontend tool calls in statewire mode.\n *\n * Tools declared through `runConfig.custom.client_tools` have no backend\n * implementation: a call parks the run and surfaces here as a statewire\n * input request. The bridge runs the matching local handler and resumes the\n * run with a per-call result envelope, so a missing tool or a thrown handler\n * still frees the run with an error the model can act on instead of hanging.\n *\n * The claim lifecycle, liveness guards, and wire-safety live in\n * `@athenaintel/agent-runtime`'s `useStatewireClientToolBridge`; this wrapper\n * only adapts the shared `RegisteredClientTool` shape and the SDK's transport.\n */\nexport function StatewireClientToolBridge({\n tools,\n threadId,\n}: {\n tools: readonly RegisteredClientTool[];\n threadId: string;\n}): null {\n const sendCommand = useStatewireSendCommand();\n const inputRequests = useStatewireRuns().inputRequests;\n\n const bridgeTools = useMemo<readonly ClientToolBridgeTool[]>(\n () =>\n tools.map((tool) => ({\n name: tool.definition.name,\n requiresWrite: tool.definition.requiresWrite,\n run: (args, context) => tool.handler(args, context),\n })),\n [tools],\n );\n\n useStatewireClientToolBridge({\n threadId,\n tools: bridgeTools,\n tracker: clientToolRequests,\n inputRequests,\n sendResume: (requestId, value) => {\n // Every envelope value is already wire-safe (per-call JSON round-trip in\n // the shared batch engine), so the batch only needs the transport's\n // JSON typing.\n return sendCommand({\n type: 'run/input',\n requestId,\n response: { type: 'resume', value: value as ReadonlyJSONObject },\n });\n },\n });\n\n return null;\n}\n"],"names":[],"mappings":";;;;AAaA,MAAM,qBAAqB,+BAAA;AAepB,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAGS;AACP,QAAM,cAAc,wBAAA;AACpB,QAAM,gBAAgB,mBAAmB;AAEzC,QAAM,cAAc;AAAA,IAClB,MACE,MAAM,IAAI,CAAC,UAAU;AAAA,MACnB,MAAM,KAAK,WAAW;AAAA,MACtB,eAAe,KAAK,WAAW;AAAA,MAC/B,KAAK,CAAC,MAAM,YAAY,KAAK,QAAQ,MAAM,OAAO;AAAA,IAAA,EAClD;AAAA,IACJ,CAAC,KAAK;AAAA,EAAA;AAGR,+BAA6B;AAAA,IAC3B;AAAA,IACA,OAAO;AAAA,IACP,SAAS;AAAA,IACT;AAAA,IACA,YAAY,CAAC,WAAW,UAAU;AAIhC,aAAO,YAAY;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QACA,UAAU,EAAE,MAAM,UAAU,MAAA;AAAA,MAAmC,CAChE;AAAA,IACH;AAAA,EAAA,CACD;AAED,SAAO;AACT;"}
|
|
@@ -3,26 +3,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const react = require("@assistant-ui/react");
|
|
5
5
|
const reactStatewire = require("@assistant-ui/react-statewire");
|
|
6
|
-
require("
|
|
6
|
+
const lucideReact = require("lucide-react");
|
|
7
7
|
const react$1 = require("react");
|
|
8
|
-
require("
|
|
9
|
-
require("
|
|
10
|
-
const errors = require("../packages/agent-runtime/src/errors.cjs");
|
|
11
|
-
require("../packages/agent-runtime/src/statewire-suspended.cjs");
|
|
12
|
-
require("../packages/agent-runtime/src/conversation-list.cjs");
|
|
13
|
-
require("@tanstack/react-query");
|
|
8
|
+
const continuePill = require("../chat-ui/messages/continue-pill.cjs");
|
|
9
|
+
const threadExtras = require("../packages/agent-runtime/src/thread-extras.cjs");
|
|
14
10
|
const statewireState = require("../packages/agent-runtime/src/statewire-state.cjs");
|
|
15
11
|
const queue = require("../packages/agent-runtime/src/queue.cjs");
|
|
16
|
-
const threadExtras = require("../packages/agent-runtime/src/thread-extras.cjs");
|
|
17
|
-
const messages = require("../packages/agent-runtime/src/messages.cjs");
|
|
18
|
-
require("@assistant-ui/store");
|
|
19
|
-
require("../packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
|
|
20
|
-
require("../packages/agent-runtime/src/statewire-attach-observer.cjs");
|
|
21
12
|
const threadStatus = require("../packages/agent-runtime/src/thread-status.cjs");
|
|
22
|
-
require("
|
|
23
|
-
require("../packages/agent-runtime/src/
|
|
24
|
-
const lucideReact = require("lucide-react");
|
|
25
|
-
const continuePill = require("../chat-ui/messages/continue-pill.cjs");
|
|
13
|
+
const errors = require("../packages/agent-runtime/src/errors.cjs");
|
|
14
|
+
const messages = require("../packages/agent-runtime/src/messages.cjs");
|
|
26
15
|
const cnfast = require("cnfast");
|
|
27
16
|
const StatewireQueuedMessages = (props) => {
|
|
28
17
|
const extras = react.useAuiState((s) => s.thread.extras);
|