@athenaintel/react 0.28.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +418 -10
- package/dist/agent-runtime/src/asset-attachment.cjs.map +1 -0
- package/dist/agent-runtime/src/asset-attachment.js.map +1 -0
- package/dist/agent-runtime/src/auth-denial.cjs.map +1 -0
- package/dist/agent-runtime/src/auth-denial.js.map +1 -0
- package/dist/agent-runtime/src/client-tool-bridge.cjs.map +1 -0
- package/dist/agent-runtime/src/client-tool-bridge.js.map +1 -0
- package/dist/agent-runtime/src/client-tools.cjs.map +1 -0
- package/dist/agent-runtime/src/client-tools.js.map +1 -0
- package/dist/agent-runtime/src/composer-markdown.cjs +124 -0
- package/dist/agent-runtime/src/composer-markdown.cjs.map +1 -0
- package/dist/agent-runtime/src/composer-markdown.js +124 -0
- package/dist/agent-runtime/src/composer-markdown.js.map +1 -0
- package/dist/agent-runtime/src/conversation-index.cjs.map +1 -0
- package/dist/agent-runtime/src/conversation-index.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/conversation-list.cjs +36 -0
- package/dist/agent-runtime/src/conversation-list.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/conversation-list.js +36 -0
- package/dist/agent-runtime/src/conversation-list.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/deep-agent-transport-recipe.cjs +5 -3
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/deep-agent-transport-recipe.js +6 -4
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -0
- package/dist/agent-runtime/src/edit-command.cjs.map +1 -0
- package/dist/agent-runtime/src/edit-command.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/errors.cjs +23 -0
- package/dist/agent-runtime/src/errors.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/errors.js +23 -0
- package/dist/agent-runtime/src/errors.js.map +1 -0
- package/dist/agent-runtime/src/execute-output.cjs +41 -0
- package/dist/agent-runtime/src/execute-output.cjs.map +1 -0
- package/dist/agent-runtime/src/execute-output.js +41 -0
- package/dist/agent-runtime/src/execute-output.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/generated/runconfig-contract.cjs +10 -2
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/generated/runconfig-contract.js +10 -2
- package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -0
- package/dist/agent-runtime/src/github-wake-event.cjs.map +1 -0
- package/dist/agent-runtime/src/github-wake-event.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/interrupts.cjs +24 -1
- package/dist/agent-runtime/src/interrupts.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/interrupts.js +24 -1
- package/dist/agent-runtime/src/interrupts.js.map +1 -0
- package/dist/agent-runtime/src/message-author.cjs.map +1 -0
- package/dist/agent-runtime/src/message-author.js.map +1 -0
- package/dist/agent-runtime/src/message-window.cjs.map +1 -0
- package/dist/agent-runtime/src/message-window.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/messages.cjs +8 -4
- package/dist/agent-runtime/src/messages.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/messages.js +6 -2
- package/dist/agent-runtime/src/messages.js.map +1 -0
- package/dist/agent-runtime/src/model-context.cjs.map +1 -0
- package/dist/agent-runtime/src/model-context.js.map +1 -0
- package/dist/agent-runtime/src/optimistic-content.cjs.map +1 -0
- package/dist/agent-runtime/src/optimistic-content.js.map +1 -0
- package/dist/agent-runtime/src/protocol.cjs.map +1 -0
- package/dist/agent-runtime/src/protocol.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/queue.cjs +23 -2
- package/dist/agent-runtime/src/queue.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/queue.js +23 -2
- package/dist/agent-runtime/src/queue.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/quota-safe-local-storage-session.cjs +23 -13
- package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/quota-safe-local-storage-session.js +24 -14
- package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -0
- package/dist/agent-runtime/src/runtime-messages.cjs.map +1 -0
- package/dist/agent-runtime/src/runtime-messages.js.map +1 -0
- package/dist/agent-runtime/src/screen-point.cjs.map +1 -0
- package/dist/agent-runtime/src/screen-point.js.map +1 -0
- package/dist/agent-runtime/src/send-recovery.cjs.map +1 -0
- package/dist/agent-runtime/src/send-recovery.js.map +1 -0
- package/dist/agent-runtime/src/statewire-attach-observer.cjs.map +1 -0
- package/dist/agent-runtime/src/statewire-attach-observer.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-state.cjs +8 -0
- package/dist/agent-runtime/src/statewire-state.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-state.js +8 -0
- package/dist/agent-runtime/src/statewire-state.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-suspended.cjs +4 -0
- package/dist/agent-runtime/src/statewire-suspended.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-suspended.js +4 -0
- package/dist/agent-runtime/src/statewire-suspended.js.map +1 -0
- package/dist/agent-runtime/src/statewire-transport.cjs +43 -0
- package/dist/agent-runtime/src/statewire-transport.cjs.map +1 -0
- package/dist/agent-runtime/src/statewire-transport.js +43 -0
- package/dist/agent-runtime/src/statewire-transport.js.map +1 -0
- package/dist/agent-runtime/src/streaming-markdown.cjs.map +1 -0
- package/dist/agent-runtime/src/streaming-markdown.js.map +1 -0
- package/dist/agent-runtime/src/subscription-wake-event.cjs +252 -0
- package/dist/agent-runtime/src/subscription-wake-event.cjs.map +1 -0
- package/dist/agent-runtime/src/subscription-wake-event.js +252 -0
- package/dist/agent-runtime/src/subscription-wake-event.js.map +1 -0
- package/dist/agent-runtime/src/thread-extras.cjs.map +1 -0
- package/dist/agent-runtime/src/thread-extras.js.map +1 -0
- package/dist/agent-runtime/src/thread-snapshot-cache.cjs.map +1 -0
- package/dist/agent-runtime/src/thread-snapshot-cache.js.map +1 -0
- package/dist/agent-runtime/src/thread-status.cjs.map +1 -0
- package/dist/agent-runtime/src/thread-status.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/tool-metadata.cjs +279 -3
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/tool-metadata.js +279 -3
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -0
- package/dist/agent-runtime/src/tool-names.cjs +5 -0
- package/dist/agent-runtime/src/tool-names.cjs.map +1 -0
- package/dist/agent-runtime/src/tool-names.js +5 -0
- package/dist/agent-runtime/src/tool-names.js.map +1 -0
- package/dist/agent-runtime/src/tool-outputs.cjs.map +1 -0
- package/dist/agent-runtime/src/tool-outputs.js.map +1 -0
- package/dist/agent-runtime/src/tool-result.cjs.map +1 -0
- package/dist/agent-runtime/src/tool-result.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/use-conversation-list.cjs +33 -22
- package/dist/agent-runtime/src/use-conversation-list.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/use-conversation-list.js +34 -23
- package/dist/agent-runtime/src/use-conversation-list.js.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-config.cjs.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-config.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-edit-submit.cjs +2 -0
- package/dist/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-edit-submit.js +2 -0
- package/dist/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-thread.cjs +1 -0
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-thread.js +2 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -0
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -0
- package/dist/agent-runtime/src/use-historical-edit-attachments.cjs +52 -0
- package/dist/agent-runtime/src/use-historical-edit-attachments.cjs.map +1 -0
- package/dist/agent-runtime/src/use-historical-edit-attachments.js +52 -0
- package/dist/agent-runtime/src/use-historical-edit-attachments.js.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/voice-presentation.cjs +14 -0
- package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -0
- package/dist/{packages/agent-runtime → agent-runtime}/src/voice-presentation.js +14 -0
- package/dist/agent-runtime/src/voice-presentation.js.map +1 -0
- package/dist/agent-runtime/src/voice-turn-grouping.cjs.map +1 -0
- package/dist/agent-runtime/src/voice-turn-grouping.js.map +1 -0
- package/dist/assets/AthenaAssetEmbed.cjs +8 -2
- package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
- package/dist/assets/AthenaAssetEmbed.d.ts +8 -0
- package/dist/assets/AthenaAssetEmbed.js +8 -2
- package/dist/assets/AthenaAssetEmbed.js.map +1 -1
- package/dist/assets/AthenaLayout.cjs +97 -31
- package/dist/assets/AthenaLayout.cjs.map +1 -1
- package/dist/assets/AthenaLayout.d.ts +13 -0
- package/dist/assets/AthenaLayout.js +98 -32
- package/dist/assets/AthenaLayout.js.map +1 -1
- package/dist/assets/asset-embed-messages.cjs.map +1 -1
- package/dist/assets/asset-embed-messages.d.ts +6 -0
- package/dist/assets/asset-embed-messages.js.map +1 -1
- package/dist/assets/asset-panel-store.cjs +68 -32
- package/dist/assets/asset-panel-store.cjs.map +1 -1
- package/dist/assets/asset-panel-store.d.ts +47 -1
- package/dist/assets/asset-panel-store.js +68 -32
- package/dist/assets/asset-panel-store.js.map +1 -1
- package/dist/assets/use-asset-embed.cjs +29 -8
- package/dist/assets/use-asset-embed.cjs.map +1 -1
- package/dist/assets/use-asset-embed.d.ts +7 -0
- package/dist/assets/use-asset-embed.js +29 -8
- package/dist/assets/use-asset-embed.js.map +1 -1
- package/dist/catalog/chat-pickers.cjs +152 -0
- package/dist/catalog/chat-pickers.cjs.map +1 -0
- package/dist/catalog/chat-pickers.d.ts +22 -0
- package/dist/catalog/chat-pickers.js +152 -0
- package/dist/catalog/chat-pickers.js.map +1 -0
- package/dist/catalog/runtime-catalog.cjs +181 -0
- package/dist/catalog/runtime-catalog.cjs.map +1 -0
- package/dist/catalog/runtime-catalog.d.ts +100 -0
- package/dist/catalog/runtime-catalog.js +181 -0
- package/dist/catalog/runtime-catalog.js.map +1 -0
- package/dist/chat/AthenaChat.cjs +86 -46
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +21 -4
- package/dist/chat/AthenaChat.js +88 -48
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireApprovalCard.cjs +38 -37
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.d.ts +2 -0
- package/dist/chat/StatewireApprovalCard.js +39 -38
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/StatewireClientToolBridge.cjs +2 -2
- package/dist/chat/StatewireClientToolBridge.js +2 -2
- package/dist/chat/StatewireQueuedMessages.cjs +15 -12
- package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
- package/dist/chat/StatewireQueuedMessages.d.ts +22 -2
- package/dist/chat/StatewireQueuedMessages.js +15 -12
- package/dist/chat/StatewireQueuedMessages.js.map +1 -1
- package/dist/chat/StatewireRunRefusalNotice.cjs +48 -0
- package/dist/chat/StatewireRunRefusalNotice.cjs.map +1 -0
- package/dist/chat/StatewireRunRefusalNotice.d.ts +25 -0
- package/dist/chat/StatewireRunRefusalNotice.js +48 -0
- package/dist/chat/StatewireRunRefusalNotice.js.map +1 -0
- package/dist/chat/WaitingOnYouBanner.cjs +2 -2
- package/dist/chat/WaitingOnYouBanner.js +2 -2
- package/dist/chat/athena-citation-links.cjs +19 -15
- package/dist/chat/athena-citation-links.cjs.map +1 -1
- package/dist/chat/athena-citation-links.js +19 -15
- package/dist/chat/athena-citation-links.js.map +1 -1
- package/dist/chat/run-activity.cjs +9 -1
- package/dist/chat/run-activity.cjs.map +1 -1
- package/dist/chat/run-activity.d.ts +6 -0
- package/dist/chat/run-activity.js +10 -2
- package/dist/chat/run-activity.js.map +1 -1
- package/dist/chat/statewire-banners.cjs +30 -1
- package/dist/chat/statewire-banners.cjs.map +1 -1
- package/dist/chat/statewire-banners.d.ts +20 -1
- package/dist/chat/statewire-banners.js +32 -3
- package/dist/chat/statewire-banners.js.map +1 -1
- package/dist/chat/statewire-client-tools.cjs +1 -1
- package/dist/chat/statewire-client-tools.js +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.cjs +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.cjs.map +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.js +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.cjs +7 -5
- package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.d.ts +8 -2
- package/dist/chat/super-grouping/SuperGroupingCard.js +7 -5
- package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +350 -0
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -0
- package/dist/chat/super-grouping/SuperGroupingTranscript.d.ts +31 -0
- package/dist/chat/super-grouping/SuperGroupingTranscript.js +350 -0
- package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -0
- package/dist/chat/super-grouping/ToolTimeline.cjs +13 -2
- package/dist/chat/super-grouping/ToolTimeline.cjs.map +1 -1
- package/dist/chat/super-grouping/ToolTimeline.d.ts +10 -3
- package/dist/chat/super-grouping/ToolTimeline.js +13 -2
- package/dist/chat/super-grouping/ToolTimeline.js.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.cjs +51 -26
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.d.ts +40 -8
- package/dist/chat/super-grouping/activity-timeline.js +52 -27
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/super-grouping/index.cjs +12 -0
- package/dist/chat/super-grouping/index.cjs.map +1 -1
- package/dist/chat/super-grouping/index.d.ts +7 -2
- package/dist/chat/super-grouping/index.js +14 -2
- package/dist/chat/super-grouping/index.js.map +1 -1
- package/dist/chat/super-grouping/timeline-step.cjs +14 -5
- package/dist/chat/super-grouping/timeline-step.cjs.map +1 -1
- package/dist/chat/super-grouping/timeline-step.d.ts +10 -1
- package/dist/chat/super-grouping/timeline-step.js +15 -6
- package/dist/chat/super-grouping/timeline-step.js.map +1 -1
- package/dist/chat/super-grouping/turn-segments.cjs +82 -0
- package/dist/chat/super-grouping/turn-segments.cjs.map +1 -0
- package/dist/chat/super-grouping/turn-segments.d.ts +69 -0
- package/dist/chat/super-grouping/turn-segments.js +82 -0
- package/dist/chat/super-grouping/turn-segments.js.map +1 -0
- package/dist/chat/super-grouping/turn-timing.cjs +110 -0
- package/dist/chat/super-grouping/turn-timing.cjs.map +1 -0
- package/dist/chat/super-grouping/turn-timing.d.ts +52 -0
- package/dist/chat/super-grouping/turn-timing.js +110 -0
- package/dist/chat/super-grouping/turn-timing.js.map +1 -0
- package/dist/chat/tiptap-text.cjs +1 -1
- package/dist/chat/tiptap-text.js +1 -1
- package/dist/chat/use-athena-run-live.cjs +1 -1
- package/dist/chat/use-athena-run-live.js +1 -1
- package/dist/chat/use-statewire-approval-request.cjs +73 -4
- package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
- package/dist/chat/use-statewire-approval-request.js +74 -5
- package/dist/chat/use-statewire-approval-request.js.map +1 -1
- package/dist/chat-ui/composer/composer-message.cjs +284 -0
- package/dist/chat-ui/composer/composer-message.cjs.map +1 -0
- package/dist/chat-ui/composer/composer-message.d.ts +152 -0
- package/dist/chat-ui/composer/composer-message.js +284 -0
- package/dist/chat-ui/composer/composer-message.js.map +1 -0
- package/dist/chat-ui/composer/tiptap-composer.cjs +57 -11
- package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
- package/dist/chat-ui/composer/tiptap-composer.d.ts +34 -6
- package/dist/chat-ui/composer/tiptap-composer.js +57 -11
- package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
- package/dist/chat-ui/index.cjs +4 -1
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +3 -2
- package/dist/chat-ui/index.js +5 -2
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.cjs +3 -2
- package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-chip.js +4 -3
- package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
- package/dist/chat-ui/mentions/mention-extension.cjs +8 -2
- package/dist/chat-ui/mentions/mention-extension.cjs.map +1 -1
- package/dist/chat-ui/mentions/mention-extension.js +8 -2
- package/dist/chat-ui/mentions/mention-extension.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.cjs +3 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.js +3 -1
- package/dist/chat-ui/mentions/suggestions/components/mention-suggestion-popup.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs +65 -19
- package/dist/chat-ui/mentions/suggestions/data/sources/index.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/sources/index.d.ts +15 -0
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js +67 -21
- package/dist/chat-ui/mentions/suggestions/data/sources/index.js.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.cjs +4 -2
- package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.cjs.map +1 -1
- package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.d.ts +2 -1
- package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.js +5 -3
- package/dist/chat-ui/mentions/suggestions/data/use-mention-suggestions.js.map +1 -1
- package/dist/chat-ui/messages/message-components.cjs +30 -10
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.d.ts +21 -1
- package/dist/chat-ui/messages/message-components.js +30 -10
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/chat-ui/messages/runtime-message.cjs +1 -1
- package/dist/chat-ui/messages/runtime-message.js +1 -1
- package/dist/chat-ui/messages/statewire-continue-run.cjs +5 -5
- package/dist/chat-ui/messages/statewire-continue-run.js +5 -5
- package/dist/chat-ui/messages/user-edit-composer.cjs +1 -1
- package/dist/chat-ui/messages/user-edit-composer.js +1 -1
- package/dist/chat-ui/messages/wake-marker.cjs +39 -4
- package/dist/chat-ui/messages/wake-marker.cjs.map +1 -1
- package/dist/chat-ui/messages/wake-marker.d.ts +16 -5
- package/dist/chat-ui/messages/wake-marker.js +40 -5
- package/dist/chat-ui/messages/wake-marker.js.map +1 -1
- package/dist/chat-ui/rich-text/chat-message-body.cjs +12 -8
- package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-message-body.d.ts +6 -1
- package/dist/chat-ui/rich-text/chat-message-body.js +13 -9
- package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
- package/dist/chat-ui/rich-text/chat-text-schema.cjs +64 -6
- package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-text-schema.d.ts +5 -0
- package/dist/chat-ui/rich-text/chat-text-schema.js +66 -8
- package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -1
- package/dist/chat-ui/statewire.cjs +1 -1
- package/dist/chat-ui/statewire.d.ts +1 -1
- package/dist/chat-ui/statewire.js +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -2
- package/dist/chat-ui/thread/statewire-history-edge.js +2 -2
- package/dist/collab/client.cjs +218 -13
- package/dist/collab/client.cjs.map +1 -1
- package/dist/collab/client.d.ts +105 -2
- package/dist/collab/client.js +221 -17
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/index.cjs +2 -0
- package/dist/collab/index.cjs.map +1 -1
- package/dist/collab/index.d.ts +1 -1
- package/dist/collab/index.js +3 -1
- package/dist/composer/AttachmentContext.cjs +38 -12
- package/dist/composer/AttachmentContext.cjs.map +1 -1
- package/dist/composer/AttachmentContext.d.ts +22 -1
- package/dist/composer/AttachmentContext.js +40 -14
- package/dist/composer/AttachmentContext.js.map +1 -1
- package/dist/composer/QuoteContext.cjs +80 -28
- package/dist/composer/QuoteContext.cjs.map +1 -1
- package/dist/composer/QuoteContext.d.ts +35 -6
- package/dist/composer/QuoteContext.js +82 -30
- package/dist/composer/QuoteContext.js.map +1 -1
- package/dist/composer/build-composed-message.cjs +27 -0
- package/dist/composer/build-composed-message.cjs.map +1 -1
- package/dist/composer/build-composed-message.d.ts +21 -0
- package/dist/composer/build-composed-message.js +28 -1
- package/dist/composer/build-composed-message.js.map +1 -1
- package/dist/composer/composer-handoff.cjs +13 -1
- package/dist/composer/composer-handoff.cjs.map +1 -1
- package/dist/composer/composer-handoff.d.ts +12 -0
- package/dist/composer/composer-handoff.js +15 -3
- package/dist/composer/composer-handoff.js.map +1 -1
- package/dist/composer/index.d.ts +6 -3
- package/dist/composer/useReadComposerDraft.cjs +16 -0
- package/dist/composer/useReadComposerDraft.cjs.map +1 -0
- package/dist/composer/useReadComposerDraft.d.ts +6 -0
- package/dist/composer/useReadComposerDraft.js +16 -0
- package/dist/composer/useReadComposerDraft.js.map +1 -0
- package/dist/diagnostics/errors.cjs +6 -0
- package/dist/diagnostics/errors.cjs.map +1 -1
- package/dist/diagnostics/errors.d.ts +6 -0
- package/dist/diagnostics/errors.js +6 -0
- package/dist/diagnostics/errors.js.map +1 -1
- package/dist/index.cjs +159 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +53 -12
- package/dist/index.js +166 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/owner-document.cjs +32 -0
- package/dist/lib/owner-document.cjs.map +1 -0
- package/dist/lib/owner-document.d.ts +17 -0
- package/dist/lib/owner-document.js +32 -0
- package/dist/lib/owner-document.js.map +1 -0
- package/dist/lib/portal-surface.cjs +55 -0
- package/dist/lib/portal-surface.cjs.map +1 -0
- package/dist/lib/portal-surface.d.ts +37 -0
- package/dist/lib/portal-surface.js +55 -0
- package/dist/lib/portal-surface.js.map +1 -0
- package/dist/lib/use-athena-rest-query.cjs +34 -0
- package/dist/lib/use-athena-rest-query.cjs.map +1 -0
- package/dist/lib/use-athena-rest-query.d.ts +31 -0
- package/dist/lib/use-athena-rest-query.js +34 -0
- package/dist/lib/use-athena-rest-query.js.map +1 -0
- package/dist/provider/AthenaProvider.cjs +36 -6
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +20 -1
- package/dist/provider/AthenaProvider.js +38 -8
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/citation-links.d.ts +10 -2
- package/dist/{packages/react → react}/package.json.cjs +1 -1
- package/dist/{packages/react → react}/package.json.js +1 -1
- package/dist/runtime/statewire-lifecycle.cjs +15 -3
- package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
- package/dist/runtime/statewire-lifecycle.d.ts +6 -0
- package/dist/runtime/statewire-lifecycle.js +15 -3
- package/dist/runtime/statewire-lifecycle.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +11 -7
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.js +11 -7
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/search/useAthenaRepoSearch.cjs +5 -13
- package/dist/search/useAthenaRepoSearch.cjs.map +1 -1
- package/dist/search/useAthenaRepoSearch.d.ts +3 -3
- package/dist/search/useAthenaRepoSearch.js +6 -14
- package/dist/search/useAthenaRepoSearch.js.map +1 -1
- package/dist/sheet-range/a1.cjs +162 -0
- package/dist/sheet-range/a1.cjs.map +1 -0
- package/dist/sheet-range/a1.d.ts +91 -0
- package/dist/sheet-range/a1.js +162 -0
- package/dist/sheet-range/a1.js.map +1 -0
- package/dist/sheet-range/index.cjs +22 -0
- package/dist/sheet-range/index.cjs.map +1 -0
- package/dist/sheet-range/index.d.ts +12 -0
- package/dist/sheet-range/index.js +22 -0
- package/dist/sheet-range/index.js.map +1 -0
- package/dist/sheet-range/range-preview-table.cjs +75 -0
- package/dist/sheet-range/range-preview-table.cjs.map +1 -0
- package/dist/sheet-range/range-preview-table.d.ts +20 -0
- package/dist/sheet-range/range-preview-table.js +75 -0
- package/dist/sheet-range/range-preview-table.js.map +1 -0
- package/dist/sheet-range/range-preview.cjs +116 -0
- package/dist/sheet-range/range-preview.cjs.map +1 -0
- package/dist/sheet-range/range-preview.d.ts +67 -0
- package/dist/sheet-range/range-preview.js +116 -0
- package/dist/sheet-range/range-preview.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/threads/Inbox.cjs +289 -0
- package/dist/threads/Inbox.cjs.map +1 -0
- package/dist/threads/Inbox.d.ts +46 -0
- package/dist/threads/Inbox.js +289 -0
- package/dist/threads/Inbox.js.map +1 -0
- package/dist/threads/SessionList.cjs +51 -126
- package/dist/threads/SessionList.cjs.map +1 -1
- package/dist/threads/SessionList.d.ts +26 -1
- package/dist/threads/SessionList.js +45 -120
- package/dist/threads/SessionList.js.map +1 -1
- package/dist/threads/SessionsPage.cjs +753 -0
- package/dist/threads/SessionsPage.cjs.map +1 -0
- package/dist/threads/SessionsPage.d.ts +35 -0
- package/dist/threads/SessionsPage.js +753 -0
- package/dist/threads/SessionsPage.js.map +1 -0
- package/dist/threads/api.cjs +21 -1
- package/dist/threads/api.cjs.map +1 -1
- package/dist/threads/api.d.ts +59 -0
- package/dist/threads/api.js +21 -1
- package/dist/threads/api.js.map +1 -1
- package/dist/threads/conversation-list-fetcher.cjs +9 -2
- package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
- package/dist/threads/conversation-list-fetcher.d.ts +14 -1
- package/dist/threads/conversation-list-fetcher.js +9 -2
- package/dist/threads/conversation-list-fetcher.js.map +1 -1
- package/dist/threads/index.d.ts +2 -1
- package/dist/threads/pull-request-display.cjs +25 -0
- package/dist/threads/pull-request-display.cjs.map +1 -0
- package/dist/threads/pull-request-display.d.ts +15 -0
- package/dist/threads/pull-request-display.js +25 -0
- package/dist/threads/pull-request-display.js.map +1 -0
- package/dist/threads/session-list-model.cjs +16 -2
- package/dist/threads/session-list-model.cjs.map +1 -1
- package/dist/threads/session-list-model.d.ts +10 -0
- package/dist/threads/session-list-model.js +16 -2
- package/dist/threads/session-list-model.js.map +1 -1
- package/dist/threads/session-row-parts.cjs +152 -0
- package/dist/threads/session-row-parts.cjs.map +1 -0
- package/dist/threads/session-row-parts.d.ts +50 -0
- package/dist/threads/session-row-parts.js +152 -0
- package/dist/threads/session-row-parts.js.map +1 -0
- package/dist/threads/sessions-page-model.cjs +305 -0
- package/dist/threads/sessions-page-model.cjs.map +1 -0
- package/dist/threads/sessions-page-model.d.ts +86 -0
- package/dist/threads/sessions-page-model.js +305 -0
- package/dist/threads/sessions-page-model.js.map +1 -0
- package/dist/threads/sessions-page-storage.cjs +93 -0
- package/dist/threads/sessions-page-storage.cjs.map +1 -0
- package/dist/threads/sessions-page-storage.d.ts +23 -0
- package/dist/threads/sessions-page-storage.js +93 -0
- package/dist/threads/sessions-page-storage.js.map +1 -0
- package/dist/threads/sessions-query.cjs +235 -0
- package/dist/threads/sessions-query.cjs.map +1 -0
- package/dist/threads/sessions-query.d.ts +119 -0
- package/dist/threads/sessions-query.js +235 -0
- package/dist/threads/sessions-query.js.map +1 -0
- package/dist/threads/statewire-thread-list.cjs +133 -40
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.d.ts +83 -2
- package/dist/threads/statewire-thread-list.js +134 -41
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/threads/use-athena-sessions.cjs +198 -0
- package/dist/threads/use-athena-sessions.cjs.map +1 -0
- package/dist/threads/use-athena-sessions.d.ts +81 -0
- package/dist/threads/use-athena-sessions.js +198 -0
- package/dist/threads/use-athena-sessions.js.map +1 -0
- package/dist/threads/use-rest-conversation-history.cjs +1 -1
- package/dist/threads/use-rest-conversation-history.js +1 -1
- package/dist/threads/use-statewire-conversation-history.cjs +1 -1
- package/dist/threads/use-statewire-conversation-history.d.ts +3 -0
- package/dist/threads/use-statewire-conversation-history.js +1 -1
- package/dist/threads/use-thread-read-state.cjs +73 -0
- package/dist/threads/use-thread-read-state.cjs.map +1 -0
- package/dist/threads/use-thread-read-state.d.ts +51 -0
- package/dist/threads/use-thread-read-state.js +73 -0
- package/dist/threads/use-thread-read-state.js.map +1 -0
- package/dist/tiptap-markdown-table/src/markdown-table-serializer.cjs.map +1 -0
- package/dist/tiptap-markdown-table/src/markdown-table-serializer.js.map +1 -0
- package/dist/tools/auto-open-tools.cjs +2 -0
- package/dist/tools/auto-open-tools.cjs.map +1 -1
- package/dist/tools/auto-open-tools.js +2 -0
- package/dist/tools/auto-open-tools.js.map +1 -1
- package/dist/tools/file-tool-uis.cjs +18 -43
- package/dist/tools/file-tool-uis.cjs.map +1 -1
- package/dist/tools/file-tool-uis.d.ts +1 -2
- package/dist/tools/file-tool-uis.js +18 -42
- package/dist/tools/file-tool-uis.js.map +1 -1
- package/dist/tools/index.cjs +6 -2
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +6 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/misc-tool-uis.cjs +2 -2
- package/dist/tools/misc-tool-uis.js +2 -2
- package/dist/tools/primitives.cjs +1 -1
- package/dist/tools/primitives.js +1 -1
- package/dist/tools/read-asset-tool-ui.cjs +21 -70
- package/dist/tools/read-asset-tool-ui.cjs.map +1 -1
- package/dist/tools/read-asset-tool-ui.js +21 -70
- package/dist/tools/read-asset-tool-ui.js.map +1 -1
- package/dist/tools/step-text.cjs +21 -0
- package/dist/tools/step-text.cjs.map +1 -0
- package/dist/tools/step-text.d.ts +10 -0
- package/dist/tools/step-text.js +21 -0
- package/dist/tools/step-text.js.map +1 -0
- package/dist/tools/tool-fallback.cjs +24 -5
- package/dist/tools/tool-fallback.cjs.map +1 -1
- package/dist/tools/tool-fallback.d.ts +3 -1
- package/dist/tools/tool-fallback.js +24 -5
- package/dist/tools/tool-fallback.js.map +1 -1
- package/dist/tools/tool-summary.cjs +661 -0
- package/dist/tools/tool-summary.cjs.map +1 -0
- package/dist/tools/tool-summary.d.ts +99 -0
- package/dist/tools/tool-summary.js +661 -0
- package/dist/tools/tool-summary.js.map +1 -0
- package/dist/tools/tool-uis.cjs +37 -95
- package/dist/tools/tool-uis.cjs.map +1 -1
- package/dist/tools/tool-uis.js +37 -95
- package/dist/tools/tool-uis.js.map +1 -1
- package/dist/tools/voice-update-tool-ui.cjs +1 -1
- package/dist/tools/voice-update-tool-ui.js +1 -1
- package/dist/tools/write-todos-tool-ui.cjs +3 -2
- package/dist/tools/write-todos-tool-ui.cjs.map +1 -1
- package/dist/tools/write-todos-tool-ui.d.ts +5 -0
- package/dist/tools/write-todos-tool-ui.js +5 -4
- package/dist/tools/write-todos-tool-ui.js.map +1 -1
- package/dist/ui/popover.cjs +6 -6
- package/dist/ui/popover.cjs.map +1 -1
- package/dist/ui/popover.js +6 -6
- package/dist/ui/popover.js.map +1 -1
- package/dist/ui/tooltip.cjs +16 -2
- package/dist/ui/tooltip.cjs.map +1 -1
- package/dist/ui/tooltip.d.ts +1 -1
- package/dist/ui/tooltip.js +16 -2
- package/dist/ui/tooltip.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist/voice/use-athena-voice-session.cjs.map +1 -1
- package/dist/voice/use-athena-voice-session.d.ts +3 -3
- package/dist/voice/use-athena-voice-session.js.map +1 -1
- package/dist/workbench/SessionWorkbench.cjs +378 -0
- package/dist/workbench/SessionWorkbench.cjs.map +1 -0
- package/dist/workbench/SessionWorkbench.d.ts +30 -0
- package/dist/workbench/SessionWorkbench.js +378 -0
- package/dist/workbench/SessionWorkbench.js.map +1 -0
- package/dist/workbench/session-workbench-context.cjs +26 -0
- package/dist/workbench/session-workbench-context.cjs.map +1 -0
- package/dist/workbench/session-workbench-context.d.ts +55 -0
- package/dist/workbench/session-workbench-context.js +26 -0
- package/dist/workbench/session-workbench-context.js.map +1 -0
- package/dist/workbench/session-workbench-model.cjs +288 -0
- package/dist/workbench/session-workbench-model.cjs.map +1 -0
- package/dist/workbench/session-workbench-model.d.ts +168 -0
- package/dist/workbench/session-workbench-model.js +288 -0
- package/dist/workbench/session-workbench-model.js.map +1 -0
- package/dist/workbench/session-workbench-panes.cjs +580 -0
- package/dist/workbench/session-workbench-panes.cjs.map +1 -0
- package/dist/workbench/session-workbench-panes.d.ts +16 -0
- package/dist/workbench/session-workbench-panes.js +580 -0
- package/dist/workbench/session-workbench-panes.js.map +1 -0
- package/dist/workbench/session-workspace-api.cjs +157 -0
- package/dist/workbench/session-workspace-api.cjs.map +1 -0
- package/dist/workbench/session-workspace-api.d.ts +70 -0
- package/dist/workbench/session-workspace-api.js +157 -0
- package/dist/workbench/session-workspace-api.js.map +1 -0
- package/dist/workbench/use-session-workspace.cjs +100 -0
- package/dist/workbench/use-session-workspace.cjs.map +1 -0
- package/dist/workbench/use-session-workspace.d.ts +45 -0
- package/dist/workbench/use-session-workspace.js +100 -0
- package/dist/workbench/use-session-workspace.js.map +1 -0
- package/dist/workbench/workbench-layout-context.cjs +10 -0
- package/dist/workbench/workbench-layout-context.cjs.map +1 -0
- package/dist/workbench/workbench-layout-context.d.ts +17 -0
- package/dist/workbench/workbench-layout-context.js +10 -0
- package/dist/workbench/workbench-layout-context.js.map +1 -0
- package/package.json +20 -9
- package/dist/node_modules/@assistant-ui/react-langchain/dist/converter.cjs +0 -77
- package/dist/node_modules/@assistant-ui/react-langchain/dist/converter.cjs.map +0 -1
- package/dist/node_modules/@assistant-ui/react-langchain/dist/converter.js +0 -77
- package/dist/node_modules/@assistant-ui/react-langchain/dist/converter.js.map +0 -1
- package/dist/node_modules/@assistant-ui/react-langgraph/dist/convertLangChainMessages.cjs +0 -181
- package/dist/node_modules/@assistant-ui/react-langgraph/dist/convertLangChainMessages.cjs.map +0 -1
- package/dist/node_modules/@assistant-ui/react-langgraph/dist/convertLangChainMessages.js +0 -181
- package/dist/node_modules/@assistant-ui/react-langgraph/dist/convertLangChainMessages.js.map +0 -1
- package/dist/packages/agent-runtime/src/asset-attachment.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/asset-attachment.js.map +0 -1
- package/dist/packages/agent-runtime/src/auth-denial.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/auth-denial.js.map +0 -1
- package/dist/packages/agent-runtime/src/client-tool-bridge.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/client-tool-bridge.js.map +0 -1
- package/dist/packages/agent-runtime/src/client-tools.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/client-tools.js.map +0 -1
- package/dist/packages/agent-runtime/src/conversation-index.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/conversation-index.js.map +0 -1
- package/dist/packages/agent-runtime/src/conversation-list.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/conversation-list.js.map +0 -1
- package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.js.map +0 -1
- package/dist/packages/agent-runtime/src/edit-command.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/edit-command.js.map +0 -1
- package/dist/packages/agent-runtime/src/errors.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/errors.js.map +0 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +0 -1
- package/dist/packages/agent-runtime/src/github-wake-event.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/github-wake-event.js.map +0 -1
- package/dist/packages/agent-runtime/src/interrupts.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/interrupts.js.map +0 -1
- package/dist/packages/agent-runtime/src/message-author.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/message-author.js.map +0 -1
- package/dist/packages/agent-runtime/src/message-window.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/message-window.js.map +0 -1
- package/dist/packages/agent-runtime/src/messages.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/messages.js.map +0 -1
- package/dist/packages/agent-runtime/src/model-context.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/model-context.js.map +0 -1
- package/dist/packages/agent-runtime/src/optimistic-content.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/optimistic-content.js.map +0 -1
- package/dist/packages/agent-runtime/src/protocol.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/protocol.js.map +0 -1
- package/dist/packages/agent-runtime/src/queue.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/queue.js.map +0 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +0 -1
- package/dist/packages/agent-runtime/src/runtime-messages.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/runtime-messages.js.map +0 -1
- package/dist/packages/agent-runtime/src/screen-point.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/screen-point.js.map +0 -1
- package/dist/packages/agent-runtime/src/send-recovery.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/send-recovery.js.map +0 -1
- package/dist/packages/agent-runtime/src/statewire-attach-observer.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/statewire-attach-observer.js.map +0 -1
- package/dist/packages/agent-runtime/src/statewire-state.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/statewire-state.js.map +0 -1
- package/dist/packages/agent-runtime/src/statewire-suspended.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/statewire-suspended.js.map +0 -1
- package/dist/packages/agent-runtime/src/statewire-transport.cjs +0 -11
- package/dist/packages/agent-runtime/src/statewire-transport.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/statewire-transport.js +0 -11
- package/dist/packages/agent-runtime/src/statewire-transport.js.map +0 -1
- package/dist/packages/agent-runtime/src/streaming-markdown.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/streaming-markdown.js.map +0 -1
- package/dist/packages/agent-runtime/src/thread-extras.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/thread-extras.js.map +0 -1
- package/dist/packages/agent-runtime/src/thread-snapshot-cache.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/thread-snapshot-cache.js.map +0 -1
- package/dist/packages/agent-runtime/src/thread-status.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/thread-status.js.map +0 -1
- package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/tool-metadata.js.map +0 -1
- package/dist/packages/agent-runtime/src/tool-outputs.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/tool-outputs.js.map +0 -1
- package/dist/packages/agent-runtime/src/tool-result.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/tool-result.js.map +0 -1
- package/dist/packages/agent-runtime/src/use-conversation-list.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/use-conversation-list.js.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-config.js.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-runtime.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-runtime.js.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.js.map +0 -1
- package/dist/packages/agent-runtime/src/voice-presentation.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/voice-presentation.js.map +0 -1
- package/dist/packages/agent-runtime/src/voice-turn-grouping.cjs.map +0 -1
- package/dist/packages/agent-runtime/src/voice-turn-grouping.js.map +0 -1
- package/dist/packages/tiptap-markdown-table/src/markdown-table-serializer.cjs.map +0 -1
- package/dist/packages/tiptap-markdown-table/src/markdown-table-serializer.js.map +0 -1
- /package/dist/{packages/agent-runtime → agent-runtime}/src/asset-attachment.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/asset-attachment.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/auth-denial.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/auth-denial.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/client-tool-bridge.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/client-tool-bridge.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/client-tools.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/client-tools.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/conversation-index.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/conversation-index.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/edit-command.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/edit-command.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/github-wake-event.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/github-wake-event.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/message-author.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/message-author.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/message-window.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/message-window.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/model-context.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/model-context.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/optimistic-content.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/optimistic-content.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/protocol.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/protocol.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/runtime-messages.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/runtime-messages.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/screen-point.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/screen-point.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/send-recovery.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/send-recovery.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-attach-observer.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/statewire-attach-observer.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/streaming-markdown.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/streaming-markdown.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-extras.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-extras.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-snapshot-cache.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-snapshot-cache.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-status.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/thread-status.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/tool-outputs.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/tool-outputs.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/tool-result.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/tool-result.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-config.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-config.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-runtime.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-runtime.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-window-sync.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/use-deep-agent-window-sync.js +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/voice-turn-grouping.cjs +0 -0
- /package/dist/{packages/agent-runtime → agent-runtime}/src/voice-turn-grouping.js +0 -0
- /package/dist/{packages/react → react}/package.json.cjs.map +0 -0
- /package/dist/{packages/react → react}/package.json.js.map +0 -0
- /package/dist/{packages/tiptap-markdown-table → tiptap-markdown-table}/src/markdown-table-serializer.cjs +0 -0
- /package/dist/{packages/tiptap-markdown-table → tiptap-markdown-table}/src/markdown-table-serializer.js +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"quota-safe-local-storage-session.js","sources":["../../../../../agent-runtime/src/quota-safe-local-storage-session.ts"],"sourcesContent":["import { resource, useResource } from \"@assistant-ui/tap\";\nimport type { StatewireClient, StatewireProtocol } from \"statewire\";\nimport { KeyValueSessionStorage } from \"statewire/storage\";\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { DEEP_AGENT_STATEWIRE_PROTOCOL } from \"./statewire-transport\";\n\nconst SESSION_PREFIX = \"aui:statewire-session:\";\nconst SESSION_LOCK_PREFIX = \"aui:statewire-session-lock:\";\nconst SESSION_ACTIVE_PREFIX = \"aui:statewire-session-active:\";\nconst DAY_MS = 24 * 60 * 60 * 1000;\n/** The stored-record formats the statewire this build ships reads (it writes 3). */\nconst READABLE_STORAGE_VERSIONS: ReadonlySet<unknown> = new Set([1, 2, 3]);\n\n/** Structural view of the browser storage global — this package compiles\n * without the DOM lib (React Native honesty; see `globals.d.ts`). */\ntype StorageLike = {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n key(index: number): string | null;\n readonly length: number;\n};\n\ntype LockGrant = object | null;\n\n/** The Web Locks API surface the session uses (`navigator.locks`). */\ntype LockManagerLike = {\n request<T>(\n name: string,\n callback: (lock: LockGrant) => T | Promise<T>,\n ): Promise<T>;\n request<T>(\n name: string,\n options: { mode?: \"shared\" | \"exclusive\"; ifAvailable?: boolean },\n callback: (lock: LockGrant) => T | Promise<T>,\n ): Promise<T>;\n};\n\ntype StorageEventLike = { key: string | null };\n\ntype BrowserGlobals = {\n localStorage?: StorageLike;\n navigator?: { locks?: LockManagerLike };\n addEventListener?: (\n type: \"storage\",\n listener: (event: StorageEventLike) => void,\n ) => void;\n removeEventListener?: (\n type: \"storage\",\n listener: (event: StorageEventLike) => void,\n ) => void;\n};\n\nconst browserGlobals = globalThis as BrowserGlobals;\n\n/** A persisted session record: per-lane failed entries (a pre-lane record carried them at the root). */\ntype SessionRecordLike = {\n failed?: unknown;\n lanes?: Record<string, { failed?: unknown } | null | undefined> | null;\n};\n\nexport function readPersistedFailedSendStamps(\n sessionKey: string,\n): ReadonlyMap<string, number> | null {\n const storage = browserGlobals.localStorage;\n if (!storage) return null;\n let record: SessionRecordLike | null;\n try {\n const raw = storage.getItem(SESSION_PREFIX + sessionKey);\n record = raw === null ? null : (JSON.parse(raw) as SessionRecordLike | null);\n } catch {\n return null;\n }\n return readFailedSendStampsFromRecord(record);\n}\n\n/**\n * Send stamps (message id → epoch ms of the original send) off an already\n * loaded session record's failed lane — the storage-agnostic half of\n * {@link readPersistedFailedSendStamps}, for hosts whose session store is not\n * localStorage (the React Native AsyncStorage-backed store reads its record\n * synchronously through `SessionStorage.load()`).\n *\n * Returns `null` for no record (every failed entry then counts as fresh) and\n * an empty map for a record without failures.\n */\nexport function readFailedSendStampsFromRecord(\n record: SessionRecordLike | null | undefined,\n): ReadonlyMap<string, number> | null {\n if (!record || typeof record !== \"object\") return null;\n const stamps = new Map<string, number>();\n const lanes = record.lanes && typeof record.lanes === \"object\" ? record.lanes : {};\n const failed = [\n ...(Array.isArray(record.failed) ? record.failed : []),\n ...Object.values(lanes).flatMap((lane) => (Array.isArray(lane?.failed) ? lane.failed : [])),\n ];\n for (const entry of failed) {\n const failed = entry as {\n ts?: unknown;\n params?: readonly { message?: { id?: unknown } }[];\n } | null;\n const messageId = failed?.params?.[0]?.message?.id;\n if (typeof messageId === \"string\" && typeof failed?.ts === \"number\") {\n stamps.set(messageId, failed.ts);\n }\n }\n return stamps;\n}\n\n/** A storage operation the session survived by degrading instead of failing. */\nexport type SessionStorageDegradation = {\n operation:\n | \"getItem\"\n | \"setItem\"\n | \"removeItem\"\n | \"getAllKeys\"\n | \"withUnused\"\n /** The lock manager refused the transaction fence; it ran unfenced. */\n | \"lock\"\n /**\n * The stored record is one this build cannot read (not JSON, or a newer\n * storage version): served as absent, and the key is shielded from being\n * overwritten so the record survives this tab.\n */\n | \"record\"\n /** statewire's own storage failure (a structurally invalid stored record). */\n | \"session\"\n /**\n * Queued commands persisted by an earlier statewire build no longer fit\n * the current wire/protocol range: parked in the lane's failed list (the\n * send-recovery sweep re-dispatches them) instead of blocking the mount.\n */\n | \"migrate\";\n key: string;\n error: unknown;\n};\n\ntype ProducedRange = NonNullable<StatewireClient.SessionCommand[\"produced\"]>;\n\nconst inRange = (value: string, range: StatewireProtocol.Range) =>\n value >= (range.minVersion ?? range.version) && value <= range.version;\n\n/** Mirrors statewire's own stored-command compatibility check. */\nconst storedCommandCompatible = (\n protocol: string,\n command: StatewireClient.SessionCommand,\n current: ProducedRange,\n): boolean => {\n if (!command.produced || !inRange(command.produced.wire.version, current.wire))\n return false;\n if (current.protocols === \"*\") return true;\n if (command.produced.protocols === \"*\") return false;\n const produced = command.produced.protocols.find((p) => p.name === protocol);\n const offer = current.protocols.find((p) => p.name === protocol);\n return produced !== undefined && offer !== undefined && inRange(produced.version, offer);\n};\n\n/**\n * statewire's `migrate` for a record whose queued commands were produced by\n * an earlier build: every incompatible command leaves its lane's queue and is\n * retained as a `lost`/`session-lost` failed send re-stamped with the current\n * range. Client identity, lane clocks, compatible commands and existing failed\n * history are untouched, so the mount proceeds and the send-recovery sweep\n * (or the user, via the failed-send UI) re-dispatches the parked sends under\n * the negotiated format instead of the runtime throwing before it dials.\n *\n * Returns the record itself when nothing needed migrating, and the count of\n * parked commands otherwise.\n */\nexport function parkIncompatibleSessionCommands(\n record: StatewireClient.SessionRecord,\n current: ProducedRange,\n now: number = Date.now(),\n): { record: StatewireClient.SessionRecord; parked: number } {\n let parked = 0;\n const lanes: Record<string, StatewireClient.SessionLane> = {};\n for (const [protocol, lane] of Object.entries(record.lanes)) {\n const kept: StatewireClient.SessionCommand[] = [];\n const failed: StatewireClient.FailedCommand[] = [];\n for (const command of lane.commands) {\n if (storedCommandCompatible(protocol, command, current)) {\n kept.push(command);\n continue;\n }\n failed.push({\n protocol,\n produced: current,\n command: command.method,\n params: command.params,\n ...(command.meta !== undefined && { meta: command.meta }),\n failure: { fate: \"lost\", cause: \"session-lost\" },\n ts: now,\n seq: command.seq,\n });\n }\n parked += failed.length;\n lanes[protocol] =\n failed.length === 0\n ? lane\n : { ...lane, commands: kept, failed: [...(lane.failed ?? []), ...failed] };\n }\n return parked === 0 ? { record, parked } : { record: { ...record, lanes }, parked };\n}\n\n/**\n * Whether `error` is the browser refusing a write for lack of room, under\n * every name the engines use — `QuotaExceededError` (Chromium, WebKit, current\n * Firefox), `NS_ERROR_DOM_QUOTA_REACHED` (older Firefox), the legacy\n * `DOMException` codes 22 (WebKit) and 1014 (Firefox) — or a plain error whose\n * message carries one of those (storage proxies and WebViews rewrap the\n * exception). Olympus's `isStorageQuotaError` recognises the same set; the two\n * stay in step.\n */\nexport function isStorageQuotaError(error: unknown): boolean {\n if (typeof error !== \"object\" || error === null) return false;\n const { name, code, message } = error as {\n name?: unknown;\n code?: unknown;\n message?: unknown;\n };\n return (\n name === \"QuotaExceededError\" ||\n name === \"NS_ERROR_DOM_QUOTA_REACHED\" ||\n code === 22 ||\n code === 1014 ||\n (typeof message === \"string\" &&\n /exceeded the quota|quotaexceedederror|ns_error_dom_quota_reached/i.test(\n message,\n ))\n );\n}\n\n/** The one quota shape statewire's retention sweep recognises. */\nfunction asStatewireQuotaError(error: unknown): Error {\n if (error instanceof Error && error.name === \"QuotaExceededError\")\n return error;\n const normalized = new Error(\n error instanceof Error ? error.message : \"storage quota exceeded\",\n );\n normalized.name = \"QuotaExceededError\";\n return normalized;\n}\n\n/**\n * Why `raw` is a stored record this build cannot read, or `undefined` when it\n * may be one. Mirrors the two decode failures statewire raises for a record it\n * cannot read — invalid JSON, and a `statewireStorageVersion` newer than the\n * ones this build's statewire decodes (a tab left open across a\n * statewire upgrade, reading the record a newer tab keeps) — because statewire\n * throws those out of the resource at mount instead of treating the record as\n * absent. A structurally invalid record of the right version still fails\n * inside statewire.\n */\nfunction unreadableRecord(raw: string): Error | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return error instanceof Error\n ? error\n : new Error(\"stored session record is not JSON\");\n }\n if (typeof parsed !== \"object\" || parsed === null) {\n return new Error(\"stored session record is not an object\");\n }\n const version = (parsed as { statewireStorageVersion?: unknown })\n .statewireStorageVersion;\n if (version !== undefined && !READABLE_STORAGE_VERSIONS.has(version)) {\n return new Error(\n `stored session record uses storage version ${String(version)}`,\n );\n }\n return undefined;\n}\n\ntype SyncBackend = {\n getItem: (key: string) => string | null;\n setItem: (key: string, value: string) => void;\n removeItem: (key: string) => void;\n getAllKeys: () => readonly string[];\n withUnused: (key: string, action: () => Promise<void>) => Promise<void>;\n};\n\n/**\n * The browser backend `@assistant-ui/react-statewire`'s `LocalStorageSession`\n * uses, reproduced here because that resource does not take a backend: raw\n * `localStorage`, with retention cleanup fenced by the same Web Locks so a\n * sweep never removes a record another tab is actively sharing.\n */\nfunction browserBackend({\n storage,\n locks,\n}: {\n storage: StorageLike;\n locks: LockManagerLike;\n}): SyncBackend {\n return {\n getItem: (key) => storage.getItem(key),\n setItem: (key, value) => storage.setItem(key, value),\n removeItem: (key) => storage.removeItem(key),\n getAllKeys: () =>\n Array.from({ length: storage.length }, (_, index) =>\n storage.key(index),\n ).filter((key): key is string => key !== null),\n withUnused: async (key, action) => {\n await locks.request(\n SESSION_ACTIVE_PREFIX + key,\n { ifAvailable: true },\n async (active) => {\n if (active === null) return;\n await locks.request(\n SESSION_LOCK_PREFIX + key.slice(SESSION_PREFIX.length),\n { ifAvailable: true },\n async (lock) => {\n if (lock === null) return;\n await action();\n },\n );\n },\n );\n },\n };\n}\n\n/**\n * Wraps a synchronous key-value backend so a failing origin degrades the\n * session to memory-only persistence instead of stopping the connection.\n *\n * statewire's session storage latches the first unrecognised write failure\n * and from then on rejects every send (`stopped/storage-error`, \"session\n * storage failed\" on the connection chip). It only recovers a\n * `QuotaExceededError` by that exact name — sweeping old session records and\n * retrying the write once — and latches again when the retry fails too. Here\n * the first quota failure per key is surfaced under that name so the sweep\n * runs; the retry that follows it and every other storage failure are absorbed\n * and reported through `onDegraded`, so chat keeps working.\n *\n * An absorbed write is kept in an in-memory overlay that `getItem` serves\n * first: statewire re-reads the record through `load()` on every shared-mode\n * transaction, so a stale persisted copy would otherwise win back its old\n * `nextSeq`, `clientId` and command journal. The overlay is per tab — another\n * tab sharing the thread still reads the last persisted copy, which is the\n * memory-only degradation this backend exists to allow. A later successful\n * write clears the overlay entry.\n *\n * A key whose read threw, or whose stored value this build cannot read (see\n * {@link unreadableRecord}), is served as absent and shielded: writes to it\n * stay in the overlay for good, so a fresh lane minted over an unreadable\n * record never overwrites what another tab or a newer build persisted.\n */\nexport function quotaTolerantBackend({\n inner,\n onDegraded,\n}: {\n inner: SyncBackend;\n onDegraded: (degradation: SessionStorageDegradation) => void;\n}): KeyValueSessionStorage.Backend {\n // Keys whose write already surfaced a quota error to statewire: until a\n // write succeeds again, later failures on the key are absorbed instead of\n // surfaced, or every save would re-run statewire's sweep (which evicts\n // other threads' idle records) and the failure would latch on the retry.\n const quotaSurfaced = new Set<string>();\n // Absorbed writes, served ahead of the backend (see the function docs).\n const overlay = new Map<string, string>();\n // Keys served as absent and never written through (see the function docs).\n const shielded = new Set<string>();\n // statewire re-reads and re-decodes the record on every transaction; the\n // readability check is memoised on the raw string so it costs one string\n // comparison per read, not a second parse.\n const checked = new Map<\n string,\n { raw: string; verdict: Error | undefined }\n >();\n const readable = (key: string, raw: string): Error | undefined => {\n const previous = checked.get(key);\n if (previous !== undefined && previous.raw === raw) return previous.verdict;\n const verdict = unreadableRecord(raw);\n checked.set(key, { raw, verdict });\n return verdict;\n };\n return {\n getItem: (key) => {\n const shadowed = overlay.get(key);\n if (shadowed !== undefined) return shadowed;\n if (shielded.has(key)) return null;\n let raw: string | null;\n try {\n raw = inner.getItem(key);\n } catch (error) {\n shielded.add(key);\n onDegraded({ operation: \"getItem\", key, error });\n return null;\n }\n if (raw === null) return null;\n const unreadable = readable(key, raw);\n if (unreadable === undefined) return raw;\n shielded.add(key);\n onDegraded({ operation: \"record\", key, error: unreadable });\n return null;\n },\n setItem: (key, value) => {\n if (shielded.has(key)) {\n overlay.set(key, value);\n return;\n }\n try {\n inner.setItem(key, value);\n overlay.delete(key);\n quotaSurfaced.delete(key);\n } catch (error) {\n if (isStorageQuotaError(error) && !quotaSurfaced.has(key)) {\n quotaSurfaced.add(key);\n throw asStatewireQuotaError(error);\n }\n overlay.set(key, value);\n onDegraded({ operation: \"setItem\", key, error });\n }\n },\n removeItem: (key) => {\n overlay.delete(key);\n if (shielded.has(key)) return;\n try {\n inner.removeItem(key);\n } catch (error) {\n onDegraded({ operation: \"removeItem\", key, error });\n }\n },\n getAllKeys: () => {\n const keys = new Set<string>(overlay.keys());\n try {\n for (const key of inner.getAllKeys()) keys.add(key);\n } catch (error) {\n onDegraded({ operation: \"getAllKeys\", key: \"\", error });\n }\n return [...keys];\n },\n withUnused: async (key, action) => {\n try {\n await inner.withUnused(key, action);\n } catch (error) {\n onDegraded({ operation: \"withUnused\", key, error });\n }\n },\n };\n}\n\ntype DegradationListener = (degradation: SessionStorageDegradation) => void;\n\ntype SharedBackend = {\n backend: KeyValueSessionStorage.Backend;\n listeners: Set<DegradationListener>;\n};\n\n/**\n * One backend per storage object — per origin, in a browser — shared by every\n * session mounted in the tab, exactly as the upstream resource's module-level\n * backend is. statewire keys its in-tab \"active\" bookkeeping by backend\n * identity, so a mount's retention sweep only skips the records of other\n * mounts on the *same* backend; and the absorbed-write overlay has to outlive\n * a remount of the same thread (a thread switch and back) or the remount would\n * read the stale persisted copy. Degradations fan out to every mounted\n * session's reporter.\n */\nconst sharedBackends = new WeakMap<StorageLike, SharedBackend>();\n\nfunction sharedBackend({\n storage,\n locks,\n}: {\n storage: StorageLike;\n locks: LockManagerLike;\n}): SharedBackend {\n let shared = sharedBackends.get(storage);\n if (shared === undefined) {\n const listeners = new Set<DegradationListener>();\n shared = {\n listeners,\n backend: quotaTolerantBackend({\n inner: browserBackend({ storage, locks }),\n onDegraded: (degradation) => {\n for (const listener of listeners) listener(degradation);\n },\n }),\n };\n sharedBackends.set(storage, shared);\n }\n return shared;\n}\n\n/**\n * Athena's browser session store: `@assistant-ui/react-statewire`'s\n * `LocalStorageSession` contract (per-thread key under the shared prefix,\n * cross-tab Web Lock, storage-event subscription, retention) on top of\n * {@link quotaTolerantBackend}, with Athena's protocol lane and retention\n * policy.\n */\nconst useQuotaSafeLocalStorageSession = ({\n threadId,\n key = (id) => id,\n ttl = 7 * DAY_MS,\n max = 50,\n onStorageDegraded,\n}: QuotaSafeLocalStorageSession.Options): StatewireClient.SessionStorage => {\n const storage = browserGlobals.localStorage;\n const locks = browserGlobals.navigator?.locks;\n if (!storage)\n throw new Error(\n \"statewire: QuotaSafeLocalStorageSession requires localStorage\",\n );\n if (!locks)\n throw new Error(\n \"statewire: QuotaSafeLocalStorageSession requires the Web Locks API (navigator.locks)\",\n );\n const sessionKey = key(threadId);\n const storageKey = SESSION_PREFIX + sessionKey;\n const lockName = SESSION_LOCK_PREFIX + sessionKey;\n\n const latestOnDegraded = useRef(onStorageDegraded);\n latestOnDegraded.current = onStorageDegraded;\n const reportDegradation = useMemo(() => {\n const reported = new Set<string>();\n return (degradation: SessionStorageDegradation) => {\n const handler = latestOnDegraded.current;\n if (handler) {\n try {\n handler(degradation);\n } catch {\n // Reporting runs inside the session's recovery path and must never\n // become the failure it is reporting.\n }\n return;\n }\n const dedupeKey = `${degradation.operation}:${degradation.key}`;\n if (reported.has(dedupeKey)) return;\n reported.add(dedupeKey);\n console.warn(\n `[statewire] session storage degraded to memory only (${degradation.operation} ${degradation.key})`,\n degradation.error,\n );\n };\n }, []);\n\n const shared = sharedBackend({ storage, locks });\n // Registered during render as well as in the effect: the read statewire\n // makes while mounting below happens before any effect runs, and tap may\n // clean an effect up and re-run it within one commit.\n shared.listeners.add(reportDegradation);\n useEffect(() => {\n shared.listeners.add(reportDegradation);\n return () => {\n shared.listeners.delete(reportDegradation);\n };\n }, [shared, reportDegradation]);\n\n // Hold the record's shared \"active\" lock while mounted so another tab's\n // retention sweep skips it (the same fence the upstream resource holds).\n useEffect(() => {\n let release: (() => void) | undefined;\n let disposed = false;\n Promise.resolve(\n locks.request(\n SESSION_ACTIVE_PREFIX + storageKey,\n { mode: \"shared\" },\n () => {\n if (disposed) return;\n return new Promise<void>((resolve) => {\n release = resolve;\n });\n },\n ),\n ).catch((error: unknown) =>\n reportDegradation({ operation: \"lock\", key: storageKey, error }),\n );\n return () => {\n disposed = true;\n release?.();\n };\n }, [locks, storageKey, reportDegradation]);\n\n const sessionStorage = useResource(\n KeyValueSessionStorage({\n backend: shared.backend,\n key: sessionKey,\n namespace: SESSION_PREFIX,\n protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,\n migrate: (record, current) => {\n const migrated = parkIncompatibleSessionCommands(record, current);\n reportDegradation({\n operation: \"migrate\",\n key: storageKey,\n error: new Error(\n `${migrated.parked} queued statewire command(s) predate the current protocol range; parked as failed sends`,\n ),\n });\n return migrated.record;\n },\n retention: { maxAgeMs: ttl, maxEntries: max },\n // A failure statewire raises itself (a structurally invalid stored\n // record) still stops the session; it is at least reported the same way.\n onError: (error) =>\n reportDegradation({ operation: \"session\", key: storageKey, error }),\n }),\n );\n\n return useMemo(\n () => ({\n ...sessionStorage,\n lock: async <T>(fn: () => T | Promise<T>): Promise<T> => {\n let granted = false;\n try {\n return await locks.request(lockName, async () => {\n granted = true;\n return fn();\n });\n } catch (error) {\n if (granted) throw error;\n // The lock manager refused the request itself (a detached or\n // back/forward-cached document, a lockdown-mode origin): the\n // transaction runs unfenced rather than stopping the session.\n reportDegradation({ operation: \"lock\", key: storageKey, error });\n return fn();\n }\n },\n subscribe: (listener: () => void) => {\n const onStorage = (event: StorageEventLike) => {\n if (event.key === storageKey) listener();\n };\n browserGlobals.addEventListener?.(\"storage\", onStorage);\n return () => browserGlobals.removeEventListener?.(\"storage\", onStorage);\n },\n }),\n [sessionStorage, locks, storageKey, lockName, reportDegradation],\n );\n};\n\n/** Athena's browser session keys and retention policy. */\nexport const QuotaSafeLocalStorageSession = resource(\n useQuotaSafeLocalStorageSession,\n);\n\nexport namespace QuotaSafeLocalStorageSession {\n export type Options = {\n threadId: string;\n key?: (threadId: string) => string;\n ttl?: number;\n max?: number;\n /**\n * Called when a storage operation failed and the session carried on in\n * memory. Defaults to one `console.warn` per operation and key.\n */\n onStorageDegraded?: (degradation: SessionStorageDegradation) => void;\n };\n}\n"],"names":["failed","_a"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-messages.cjs","sources":["../../../../../agent-runtime/src/runtime-messages.ts"],"sourcesContent":["/**\n * Typed runtime messages — the one description of \"the runtime ended this run\"\n * that every chat surface renders from.\n *\n * The deep-agent backend records how a run ended in two shapes that ride\n * ordinary `ai` messages (a new LangChain message type would crash the shipped\n * converter, whose convertible set is closed):\n *\n * - a **run-lifecycle marker** (`athena_run_marker`: `cancelled` / `failed`,\n * plus `athena_run_marker_source`) — see {@link readRunMarkerFromSourceMessages};\n * - a **handoff message** (`athena_handoff: true`) — the closing summary the\n * runtime asked the model to write when a guard or the step budget ended the\n * run, instead of fabricating an internal sentence.\n *\n * Both now carry the canonical `athena_termination_reason`\n * (`agora/graphs/athena_deep_agent/termination.py`). This module reads all of\n * that into one {@link AthenaRuntimeMessage} so Olympus, mobile, the Chrome\n * extension, and the Office add-in agree on kind, severity, copy, and the\n * sole-vs-grouped distinction — the last of which is load-bearing: a marker\n * appended after the assistant's own work is chunked into that message, and\n * replacing the whole row would hide completed prose and tool cards.\n */\n\nimport { getExternalStoreMessages } from '@assistant-ui/core';\nimport type { LangChainMessage } from '@assistant-ui/react-langgraph';\nimport {\n ATHENA_HANDOFF_KEY,\n ATHENA_RUN_MARKER_CANCELLED,\n ATHENA_RUN_MARKER_FAILED,\n ATHENA_RUN_MARKER_KEY,\n ATHENA_RUN_MARKER_SOURCE_KEY,\n ATHENA_STOP_SOURCE_SEND_NOW,\n ATHENA_TERMINATION_REASON_KEY,\n isServerUpdateStopSource,\n} from './messages';\n\n/** The backend's canonical reason vocabulary (`KNOWN_TERMINATION_REASONS`). */\nexport const TERMINATION_REASONS = [\n 'completed',\n 'user_stop',\n 'send_now',\n 'voice_steer',\n 'transport_cancel',\n 'pod_drain',\n 'fence_lost',\n 'instance_closed',\n 'unattributed_stop',\n 'recursion_limit',\n 'step_budget',\n 'duplicate_call_guard',\n 'synthetic_stop',\n 'ended_after_tools',\n 'error',\n 'model_refusal',\n 'abandoned',\n] as const;\n\nexport type AthenaTerminationReason = (typeof TERMINATION_REASONS)[number];\n\n/**\n * How a runtime ending renders:\n * - `stopped` — a cancellation the user (or a client action) asked for;\n * - `paused` — a pod-drain pause; the run resumes from its checkpoint;\n * - `failed` — the graph raised, hit the recursion limit, or was abandoned;\n * - `handoff` — the runtime ended the run and the model wrote a closing\n * summary (the message text), which renders as prose under a banner.\n */\nexport type AthenaRuntimeMessageKind = 'stopped' | 'paused' | 'failed' | 'handoff';\n\nexport type AthenaRuntimeMessageSeverity = 'info' | 'warning' | 'error';\n\nexport interface TerminationDescription {\n /** Short label for the banner / status row. */\n title: string;\n /** One sentence of reason-specific explanation, when it adds information. */\n detail: string | undefined;\n}\n\nexport interface AthenaRuntimeMessage {\n kind: AthenaRuntimeMessageKind;\n /** Canonical reason (may be a value newer than this client knows). */\n reason: string | undefined;\n severity: AthenaRuntimeMessageSeverity;\n title: string;\n detail: string | undefined;\n /** The message's own text: a marker's backend copy, or the handoff summary. */\n text: string | undefined;\n /**\n * A `send_now` promotion cancels the live run as plumbing, not as a\n * user-visible stop: keep it in the data, paint nothing.\n */\n silent: boolean;\n /**\n * True when the runtime message is the ONLY source message behind this\n * thread message (it owns the whole row). False when the chunk also holds\n * the assistant's own work, which must keep rendering.\n */\n isSoleSourceMessage: boolean;\n}\n\nconst MAX_STEPS_HINT = 'Raise “Max steps” in the agent settings for longer runs.';\n\n/**\n * Copy for a runtime ending. Reasons this client does not know fall back to\n * the kind's generic title, so a newer backend never renders as an empty row.\n */\nexport function describeTerminationReason(\n kind: AthenaRuntimeMessageKind,\n reason: string | undefined\n): TerminationDescription {\n switch (kind) {\n case 'paused':\n return {\n title: 'Paused by a server update',\n detail: 'Progress up to this point is saved — send a message to continue.',\n };\n case 'stopped':\n switch (reason) {\n case 'voice_steer':\n return {\n title: 'Redirected by a spoken follow-up',\n detail:\n 'The voice conversation changed the request; the work continues with the new instructions.',\n };\n case 'transport_cancel':\n return {\n title: 'Run cancelled',\n detail:\n 'Cancelled by a page reload, navigation, or closed view — not an explicit Stop. Send a follow-up to continue.',\n };\n case 'fence_lost':\n return {\n title: 'Run superseded',\n detail: 'Another activation took over this thread.',\n };\n case 'instance_closed':\n return {\n title: 'Run stopped',\n detail: 'The client that started it went away.',\n };\n default:\n return { title: 'Conversation stopped', detail: undefined };\n }\n case 'failed':\n switch (reason) {\n case 'recursion_limit':\n return {\n title: 'Step limit reached',\n detail: `The run was stopped before finishing; the work so far is saved. ${MAX_STEPS_HINT}`,\n };\n case 'abandoned':\n return {\n title: 'Run stopped unexpectedly',\n detail:\n 'The server handling it went away mid-run (a crash, restart, or deploy). The work so far is saved.',\n };\n default:\n return { title: 'Run failed', detail: undefined };\n }\n case 'handoff':\n switch (reason) {\n case 'duplicate_call_guard':\n return {\n title: 'Run ended by the runtime',\n detail:\n 'The same tool calls were repeating with identical arguments and no new progress, so the agent was asked to wrap up.',\n };\n case 'step_budget':\n return {\n title: 'Run ended by the runtime',\n detail: `The step budget ran out, so the agent was asked to wrap up. ${MAX_STEPS_HINT}`,\n };\n default:\n return {\n title: 'Run ended by the runtime',\n detail: 'The agent was asked to wrap up.',\n };\n }\n }\n}\n\nconst SEVERITY: Record<AthenaRuntimeMessageKind, AthenaRuntimeMessageSeverity> = {\n stopped: 'info',\n paused: 'info',\n failed: 'error',\n handoff: 'warning',\n};\n\ntype Kwargs = Record<string, unknown> | undefined;\n\nfunction metadataOf(kwargs: Kwargs): Record<string, unknown> | undefined {\n const metadata = kwargs?.metadata;\n return typeof metadata === 'object' && metadata !== null\n ? (metadata as Record<string, unknown>)\n : undefined;\n}\n\n/** The termination reason stamped on `additional_kwargs` (top-level or mirror). */\nexport function readTerminationReason(kwargs: Kwargs): string | undefined {\n const top = kwargs?.[ATHENA_TERMINATION_REASON_KEY];\n if (typeof top === 'string') return top;\n const mirrored = metadataOf(kwargs)?.[ATHENA_TERMINATION_REASON_KEY];\n return typeof mirrored === 'string' ? mirrored : undefined;\n}\n\nfunction readHandoff(kwargs: Kwargs): boolean {\n return kwargs?.[ATHENA_HANDOFF_KEY] === true || metadataOf(kwargs)?.[ATHENA_HANDOFF_KEY] === true;\n}\n\nfunction textOf(content: unknown): string | undefined {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return undefined;\n const texts = content\n .map((block) =>\n typeof block === 'string'\n ? block\n : block && typeof block === 'object' && (block as { type?: unknown }).type === 'text'\n ? (block as { text?: unknown }).text\n : undefined\n )\n .filter((text): text is string => typeof text === 'string' && text.length > 0);\n return texts.length > 0 ? texts.join('\\n') : undefined;\n}\n\nfunction fromMarker(\n marker: string,\n source: string | undefined,\n reason: string | undefined\n): Pick<AthenaRuntimeMessage, 'kind' | 'reason' | 'silent'> | undefined {\n if (marker === ATHENA_RUN_MARKER_CANCELLED) {\n const kind: AthenaRuntimeMessageKind = isServerUpdateStopSource(source) ? 'paused' : 'stopped';\n return {\n kind,\n reason: reason ?? (source === 'unattributed' ? 'unattributed_stop' : source),\n silent: source === ATHENA_STOP_SOURCE_SEND_NOW,\n };\n }\n if (marker === ATHENA_RUN_MARKER_FAILED) {\n return { kind: 'failed', reason: reason ?? 'error', silent: false };\n }\n return undefined;\n}\n\nfunction build(\n partial: Pick<AthenaRuntimeMessage, 'kind' | 'reason' | 'silent'>,\n text: string | undefined,\n isSoleSourceMessage: boolean\n): AthenaRuntimeMessage {\n const description = describeTerminationReason(partial.kind, partial.reason);\n return {\n kind: partial.kind,\n reason: partial.reason,\n severity: SEVERITY[partial.kind],\n title: description.title,\n detail: description.detail,\n text,\n silent: partial.silent,\n isSoleSourceMessage,\n };\n}\n\n/**\n * Read the runtime ending behind a thread message, from its bound source\n * messages first (a marker or handoff chunked after the assistant's own turn\n * is never the first source, so the joined `metadata.custom` would miss it —\n * see {@link readRunMarkerFromSourceMessages}), then from joined metadata for\n * surfaces without bound sources.\n */\nexport function readRuntimeMessageFromSourceMessages(\n message: Parameters<typeof getExternalStoreMessages>[0] & {\n metadata?: { custom?: Record<string, unknown> | undefined } | undefined;\n content?: unknown;\n }\n): AthenaRuntimeMessage | undefined {\n const sources = getExternalStoreMessages<LangChainMessage>(message);\n for (const source of sources) {\n if (source?.type !== 'ai') continue;\n const kwargs = source.additional_kwargs as Kwargs;\n const marker = kwargs?.[ATHENA_RUN_MARKER_KEY];\n const reason = readTerminationReason(kwargs);\n const text = textOf(source.content);\n if (typeof marker === 'string') {\n const markerSource = kwargs?.[ATHENA_RUN_MARKER_SOURCE_KEY];\n const partial = fromMarker(\n marker,\n typeof markerSource === 'string' ? markerSource : undefined,\n reason\n );\n if (partial) return build(partial, text, sources.length === 1);\n }\n if (readHandoff(kwargs)) {\n return build({ kind: 'handoff', reason, silent: false }, text, sources.length === 1);\n }\n }\n const custom = message.metadata?.custom;\n if (!custom) return undefined;\n const parts = message.content;\n const sole = Array.isArray(parts) ? parts.length <= 1 : true;\n const joinedMarker = custom[ATHENA_RUN_MARKER_KEY];\n const joinedReason = readTerminationReason({ metadata: custom });\n if (typeof joinedMarker === 'string') {\n const joinedSource = custom[ATHENA_RUN_MARKER_SOURCE_KEY];\n const partial = fromMarker(\n joinedMarker,\n typeof joinedSource === 'string' ? joinedSource : undefined,\n joinedReason\n );\n if (partial) return build(partial, undefined, sole);\n }\n if (custom[ATHENA_HANDOFF_KEY] === true) {\n return build({ kind: 'handoff', reason: joinedReason, silent: false }, undefined, sole);\n }\n return undefined;\n}\n\n/**\n * The backend marker copy worth showing under a runtime card, or `undefined`.\n *\n * A failure marker's text carries the specific error (\"The run failed:\n * provider returned 502\"), so it is shown when no reason-specific detail\n * exists. Stop/pause copy only restates the title, and a handoff's text is the\n * message body itself, so neither is repeated. Marker copy leads with an emoji\n * for raw-transcript readers; the card's icon plays that role, so it is\n * stripped. Shared by every presenter so mobile and DOM cannot drift.\n */\nexport function runtimeMessageMarkerText(message: AthenaRuntimeMessage): string | undefined {\n if (message.kind !== 'failed' || message.detail) return undefined;\n const text = message.text?.replace(/^(?:⏹️|⏸️|⚠️|\\s)+/u, '').trim();\n if (!text || text === message.title) return undefined;\n return text;\n}\n\n/**\n * The runtime message's kind when it owns the whole row, else `undefined`.\n *\n * Returns a primitive so it is safe as a `useAuiState` selector (the reader\n * above builds a fresh object per call; an object snapshot would loop the\n * render under `Object.is` comparison).\n */\nexport function readSoleRuntimeMessageKind(\n message: Parameters<typeof readRuntimeMessageFromSourceMessages>[0]\n): AthenaRuntimeMessageKind | undefined {\n const found = readRuntimeMessageFromSourceMessages(message);\n return found?.isSoleSourceMessage ? found.kind : undefined;\n}\n"],"names":["ATHENA_TERMINATION_REASON_KEY","ATHENA_HANDOFF_KEY","ATHENA_RUN_MARKER_CANCELLED","isServerUpdateStopSource","ATHENA_STOP_SOURCE_SEND_NOW","ATHENA_RUN_MARKER_FAILED","getExternalStoreMessages","ATHENA_RUN_MARKER_KEY","ATHENA_RUN_MARKER_SOURCE_KEY"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-messages.js","sources":["../../../../../agent-runtime/src/runtime-messages.ts"],"sourcesContent":["/**\n * Typed runtime messages — the one description of \"the runtime ended this run\"\n * that every chat surface renders from.\n *\n * The deep-agent backend records how a run ended in two shapes that ride\n * ordinary `ai` messages (a new LangChain message type would crash the shipped\n * converter, whose convertible set is closed):\n *\n * - a **run-lifecycle marker** (`athena_run_marker`: `cancelled` / `failed`,\n * plus `athena_run_marker_source`) — see {@link readRunMarkerFromSourceMessages};\n * - a **handoff message** (`athena_handoff: true`) — the closing summary the\n * runtime asked the model to write when a guard or the step budget ended the\n * run, instead of fabricating an internal sentence.\n *\n * Both now carry the canonical `athena_termination_reason`\n * (`agora/graphs/athena_deep_agent/termination.py`). This module reads all of\n * that into one {@link AthenaRuntimeMessage} so Olympus, mobile, the Chrome\n * extension, and the Office add-in agree on kind, severity, copy, and the\n * sole-vs-grouped distinction — the last of which is load-bearing: a marker\n * appended after the assistant's own work is chunked into that message, and\n * replacing the whole row would hide completed prose and tool cards.\n */\n\nimport { getExternalStoreMessages } from '@assistant-ui/core';\nimport type { LangChainMessage } from '@assistant-ui/react-langgraph';\nimport {\n ATHENA_HANDOFF_KEY,\n ATHENA_RUN_MARKER_CANCELLED,\n ATHENA_RUN_MARKER_FAILED,\n ATHENA_RUN_MARKER_KEY,\n ATHENA_RUN_MARKER_SOURCE_KEY,\n ATHENA_STOP_SOURCE_SEND_NOW,\n ATHENA_TERMINATION_REASON_KEY,\n isServerUpdateStopSource,\n} from './messages';\n\n/** The backend's canonical reason vocabulary (`KNOWN_TERMINATION_REASONS`). */\nexport const TERMINATION_REASONS = [\n 'completed',\n 'user_stop',\n 'send_now',\n 'voice_steer',\n 'transport_cancel',\n 'pod_drain',\n 'fence_lost',\n 'instance_closed',\n 'unattributed_stop',\n 'recursion_limit',\n 'step_budget',\n 'duplicate_call_guard',\n 'synthetic_stop',\n 'ended_after_tools',\n 'error',\n 'model_refusal',\n 'abandoned',\n] as const;\n\nexport type AthenaTerminationReason = (typeof TERMINATION_REASONS)[number];\n\n/**\n * How a runtime ending renders:\n * - `stopped` — a cancellation the user (or a client action) asked for;\n * - `paused` — a pod-drain pause; the run resumes from its checkpoint;\n * - `failed` — the graph raised, hit the recursion limit, or was abandoned;\n * - `handoff` — the runtime ended the run and the model wrote a closing\n * summary (the message text), which renders as prose under a banner.\n */\nexport type AthenaRuntimeMessageKind = 'stopped' | 'paused' | 'failed' | 'handoff';\n\nexport type AthenaRuntimeMessageSeverity = 'info' | 'warning' | 'error';\n\nexport interface TerminationDescription {\n /** Short label for the banner / status row. */\n title: string;\n /** One sentence of reason-specific explanation, when it adds information. */\n detail: string | undefined;\n}\n\nexport interface AthenaRuntimeMessage {\n kind: AthenaRuntimeMessageKind;\n /** Canonical reason (may be a value newer than this client knows). */\n reason: string | undefined;\n severity: AthenaRuntimeMessageSeverity;\n title: string;\n detail: string | undefined;\n /** The message's own text: a marker's backend copy, or the handoff summary. */\n text: string | undefined;\n /**\n * A `send_now` promotion cancels the live run as plumbing, not as a\n * user-visible stop: keep it in the data, paint nothing.\n */\n silent: boolean;\n /**\n * True when the runtime message is the ONLY source message behind this\n * thread message (it owns the whole row). False when the chunk also holds\n * the assistant's own work, which must keep rendering.\n */\n isSoleSourceMessage: boolean;\n}\n\nconst MAX_STEPS_HINT = 'Raise “Max steps” in the agent settings for longer runs.';\n\n/**\n * Copy for a runtime ending. Reasons this client does not know fall back to\n * the kind's generic title, so a newer backend never renders as an empty row.\n */\nexport function describeTerminationReason(\n kind: AthenaRuntimeMessageKind,\n reason: string | undefined\n): TerminationDescription {\n switch (kind) {\n case 'paused':\n return {\n title: 'Paused by a server update',\n detail: 'Progress up to this point is saved — send a message to continue.',\n };\n case 'stopped':\n switch (reason) {\n case 'voice_steer':\n return {\n title: 'Redirected by a spoken follow-up',\n detail:\n 'The voice conversation changed the request; the work continues with the new instructions.',\n };\n case 'transport_cancel':\n return {\n title: 'Run cancelled',\n detail:\n 'Cancelled by a page reload, navigation, or closed view — not an explicit Stop. Send a follow-up to continue.',\n };\n case 'fence_lost':\n return {\n title: 'Run superseded',\n detail: 'Another activation took over this thread.',\n };\n case 'instance_closed':\n return {\n title: 'Run stopped',\n detail: 'The client that started it went away.',\n };\n default:\n return { title: 'Conversation stopped', detail: undefined };\n }\n case 'failed':\n switch (reason) {\n case 'recursion_limit':\n return {\n title: 'Step limit reached',\n detail: `The run was stopped before finishing; the work so far is saved. ${MAX_STEPS_HINT}`,\n };\n case 'abandoned':\n return {\n title: 'Run stopped unexpectedly',\n detail:\n 'The server handling it went away mid-run (a crash, restart, or deploy). The work so far is saved.',\n };\n default:\n return { title: 'Run failed', detail: undefined };\n }\n case 'handoff':\n switch (reason) {\n case 'duplicate_call_guard':\n return {\n title: 'Run ended by the runtime',\n detail:\n 'The same tool calls were repeating with identical arguments and no new progress, so the agent was asked to wrap up.',\n };\n case 'step_budget':\n return {\n title: 'Run ended by the runtime',\n detail: `The step budget ran out, so the agent was asked to wrap up. ${MAX_STEPS_HINT}`,\n };\n default:\n return {\n title: 'Run ended by the runtime',\n detail: 'The agent was asked to wrap up.',\n };\n }\n }\n}\n\nconst SEVERITY: Record<AthenaRuntimeMessageKind, AthenaRuntimeMessageSeverity> = {\n stopped: 'info',\n paused: 'info',\n failed: 'error',\n handoff: 'warning',\n};\n\ntype Kwargs = Record<string, unknown> | undefined;\n\nfunction metadataOf(kwargs: Kwargs): Record<string, unknown> | undefined {\n const metadata = kwargs?.metadata;\n return typeof metadata === 'object' && metadata !== null\n ? (metadata as Record<string, unknown>)\n : undefined;\n}\n\n/** The termination reason stamped on `additional_kwargs` (top-level or mirror). */\nexport function readTerminationReason(kwargs: Kwargs): string | undefined {\n const top = kwargs?.[ATHENA_TERMINATION_REASON_KEY];\n if (typeof top === 'string') return top;\n const mirrored = metadataOf(kwargs)?.[ATHENA_TERMINATION_REASON_KEY];\n return typeof mirrored === 'string' ? mirrored : undefined;\n}\n\nfunction readHandoff(kwargs: Kwargs): boolean {\n return kwargs?.[ATHENA_HANDOFF_KEY] === true || metadataOf(kwargs)?.[ATHENA_HANDOFF_KEY] === true;\n}\n\nfunction textOf(content: unknown): string | undefined {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return undefined;\n const texts = content\n .map((block) =>\n typeof block === 'string'\n ? block\n : block && typeof block === 'object' && (block as { type?: unknown }).type === 'text'\n ? (block as { text?: unknown }).text\n : undefined\n )\n .filter((text): text is string => typeof text === 'string' && text.length > 0);\n return texts.length > 0 ? texts.join('\\n') : undefined;\n}\n\nfunction fromMarker(\n marker: string,\n source: string | undefined,\n reason: string | undefined\n): Pick<AthenaRuntimeMessage, 'kind' | 'reason' | 'silent'> | undefined {\n if (marker === ATHENA_RUN_MARKER_CANCELLED) {\n const kind: AthenaRuntimeMessageKind = isServerUpdateStopSource(source) ? 'paused' : 'stopped';\n return {\n kind,\n reason: reason ?? (source === 'unattributed' ? 'unattributed_stop' : source),\n silent: source === ATHENA_STOP_SOURCE_SEND_NOW,\n };\n }\n if (marker === ATHENA_RUN_MARKER_FAILED) {\n return { kind: 'failed', reason: reason ?? 'error', silent: false };\n }\n return undefined;\n}\n\nfunction build(\n partial: Pick<AthenaRuntimeMessage, 'kind' | 'reason' | 'silent'>,\n text: string | undefined,\n isSoleSourceMessage: boolean\n): AthenaRuntimeMessage {\n const description = describeTerminationReason(partial.kind, partial.reason);\n return {\n kind: partial.kind,\n reason: partial.reason,\n severity: SEVERITY[partial.kind],\n title: description.title,\n detail: description.detail,\n text,\n silent: partial.silent,\n isSoleSourceMessage,\n };\n}\n\n/**\n * Read the runtime ending behind a thread message, from its bound source\n * messages first (a marker or handoff chunked after the assistant's own turn\n * is never the first source, so the joined `metadata.custom` would miss it —\n * see {@link readRunMarkerFromSourceMessages}), then from joined metadata for\n * surfaces without bound sources.\n */\nexport function readRuntimeMessageFromSourceMessages(\n message: Parameters<typeof getExternalStoreMessages>[0] & {\n metadata?: { custom?: Record<string, unknown> | undefined } | undefined;\n content?: unknown;\n }\n): AthenaRuntimeMessage | undefined {\n const sources = getExternalStoreMessages<LangChainMessage>(message);\n for (const source of sources) {\n if (source?.type !== 'ai') continue;\n const kwargs = source.additional_kwargs as Kwargs;\n const marker = kwargs?.[ATHENA_RUN_MARKER_KEY];\n const reason = readTerminationReason(kwargs);\n const text = textOf(source.content);\n if (typeof marker === 'string') {\n const markerSource = kwargs?.[ATHENA_RUN_MARKER_SOURCE_KEY];\n const partial = fromMarker(\n marker,\n typeof markerSource === 'string' ? markerSource : undefined,\n reason\n );\n if (partial) return build(partial, text, sources.length === 1);\n }\n if (readHandoff(kwargs)) {\n return build({ kind: 'handoff', reason, silent: false }, text, sources.length === 1);\n }\n }\n const custom = message.metadata?.custom;\n if (!custom) return undefined;\n const parts = message.content;\n const sole = Array.isArray(parts) ? parts.length <= 1 : true;\n const joinedMarker = custom[ATHENA_RUN_MARKER_KEY];\n const joinedReason = readTerminationReason({ metadata: custom });\n if (typeof joinedMarker === 'string') {\n const joinedSource = custom[ATHENA_RUN_MARKER_SOURCE_KEY];\n const partial = fromMarker(\n joinedMarker,\n typeof joinedSource === 'string' ? joinedSource : undefined,\n joinedReason\n );\n if (partial) return build(partial, undefined, sole);\n }\n if (custom[ATHENA_HANDOFF_KEY] === true) {\n return build({ kind: 'handoff', reason: joinedReason, silent: false }, undefined, sole);\n }\n return undefined;\n}\n\n/**\n * The backend marker copy worth showing under a runtime card, or `undefined`.\n *\n * A failure marker's text carries the specific error (\"The run failed:\n * provider returned 502\"), so it is shown when no reason-specific detail\n * exists. Stop/pause copy only restates the title, and a handoff's text is the\n * message body itself, so neither is repeated. Marker copy leads with an emoji\n * for raw-transcript readers; the card's icon plays that role, so it is\n * stripped. Shared by every presenter so mobile and DOM cannot drift.\n */\nexport function runtimeMessageMarkerText(message: AthenaRuntimeMessage): string | undefined {\n if (message.kind !== 'failed' || message.detail) return undefined;\n const text = message.text?.replace(/^(?:⏹️|⏸️|⚠️|\\s)+/u, '').trim();\n if (!text || text === message.title) return undefined;\n return text;\n}\n\n/**\n * The runtime message's kind when it owns the whole row, else `undefined`.\n *\n * Returns a primitive so it is safe as a `useAuiState` selector (the reader\n * above builds a fresh object per call; an object snapshot would loop the\n * render under `Object.is` comparison).\n */\nexport function readSoleRuntimeMessageKind(\n message: Parameters<typeof readRuntimeMessageFromSourceMessages>[0]\n): AthenaRuntimeMessageKind | undefined {\n const found = readRuntimeMessageFromSourceMessages(message);\n return found?.isSoleSourceMessage ? found.kind : undefined;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"screen-point.cjs","sources":["../../../../../agent-runtime/src/screen-point.ts"],"sourcesContent":["/**\n * Screen points: the agent's on-screen coordinates as structured, renderable\n * data.\n *\n * Browser agents already speak in `[x, y]` pairs — the `coordinate` argument\n * of `chrome_computer` / `device_computer` / `computer` is a pixel in the\n * screenshot the model was shown. In prose those pairs arrive as free text\n * (\"Delete at ~(1038, 81)\"), which no surface can render as anything but\n * characters. A screen point is that same pair written as a markdown link\n * whose destination carries the pair *and the dimensions of the screenshot it\n * was read from*:\n *\n * [Delete](athena-point://screenshot?x=1038&y=81&w=1280&h=720&tab=8811)\n *\n * The agent keeps its native vocabulary (the coordinate it already has, the\n * screenshot size it was told) and does no arithmetic; every chat surface\n * that understands the scheme renders the link as a chip and, because the\n * basis dimensions travel with the point, can place a marker at the right\n * relative spot on any later rendition of that screenshot however the\n * transcript is laid out or the user's window is resized. Surfaces that do\n * not understand the scheme show the label as plain text (a link with an\n * unknown protocol), which is exactly the prose we have today — the format\n * degrades to the status quo, never below it.\n *\n * Pure string and number work, UI-library-free, shared by every chat surface\n * (Olympus, the React SDK hosts, the Chrome extension side panel) so a point\n * that is valid on one surface is valid on all of them.\n */\n\n/** URL scheme of a screen-point link. */\nexport const SCREEN_POINT_PROTOCOL = \"athena-point:\";\n/** Host of a screen-point link: the coordinate space the pair lives in. */\nexport const SCREEN_POINT_HOST = \"screenshot\";\n/** `href.startsWith(SCREEN_POINT_HREF_PREFIX)` is the cheap pre-check every renderer runs. */\nexport const SCREEN_POINT_HREF_PREFIX = `${SCREEN_POINT_PROTOCOL}//`;\n/**\n * The largest coordinate or dimension a point may carry. No screenshot the\n * runtime delivers approaches this; anything larger is a typo or a unit mix-up.\n */\nexport const SCREEN_POINT_MAX_EXTENT = 32_767;\n/**\n * The model-facing CUA screenshot size the Chrome extension emits by default\n * (`athena-in-chrome/extension/src/background.ts`, `CHROME_DEFAULT_WIDTH` /\n * `CHROME_DEFAULT_HEIGHT` in `chrome_browser_tools.py`). Surfaces may offer\n * it as the fallback basis for a point that omits `w`/`h`, clearly labelled\n * as assumed.\n */\nexport const DEFAULT_CUA_SCREENSHOT_SIZE = { width: 1280, height: 720 } as const;\n\n/** Width and height of a coordinate space, in that space's pixels. */\nexport interface ScreenSize {\n width: number;\n height: number;\n}\n\n/**\n * A point in screenshot space. `basis` is the size of the screenshot the pair\n * was read from; `null` when the link omitted it, in which case the point can\n * be shown but not placed. `tabId` is the Chrome tab the screenshot came from\n * when the agent had one.\n */\nexport interface ScreenPoint {\n x: number;\n y: number;\n basis: ScreenSize | null;\n tabId: number | null;\n}\n\n/**\n * Why a link that starts with the scheme could not become a point. Rendered\n * surfaces show the label as text with the reason in a tooltip, so a broken\n * link never becomes an interactive marker that points nowhere.\n */\nexport type ScreenPointParseError =\n | \"not-screen-point\"\n | \"missing-coordinate\"\n | \"non-finite-number\"\n | \"negative-number\"\n | \"degenerate-basis\"\n | \"out-of-bounds\"\n | \"extent-too-large\";\n\nexport type ScreenPointParseResult =\n | { ok: true; point: ScreenPoint }\n | { ok: false; error: ScreenPointParseError };\n\n/** A number the agent may have written with a decimal point or a `~`: keep the integer pixel. */\nconst NUMBER_PATTERN = /^~?\\s*(-?\\d+(?:\\.\\d+)?)$/;\n\ntype NumberParse =\n | { ok: true; value: number }\n | { ok: false; error: \"non-finite-number\" | \"negative-number\" | \"extent-too-large\" }\n | { ok: false; error: \"missing\" };\n\nfunction parseCoordinateNumber(raw: string | null): NumberParse {\n if (raw === null || raw.trim() === \"\") return { ok: false, error: \"missing\" };\n const match = NUMBER_PATTERN.exec(raw.trim());\n if (!match) return { ok: false, error: \"non-finite-number\" };\n const value = Number(match[1]);\n if (!Number.isFinite(value)) return { ok: false, error: \"non-finite-number\" };\n if (value < 0) return { ok: false, error: \"negative-number\" };\n if (value > SCREEN_POINT_MAX_EXTENT) return { ok: false, error: \"extent-too-large\" };\n return { ok: true, value: Math.round(value) };\n}\n\n/** Whether `href` is a screen-point link at all (the cheap pre-check). */\nexport function isScreenPointHref(href: string | null | undefined): href is string {\n return typeof href === \"string\" && href.startsWith(SCREEN_POINT_HREF_PREFIX);\n}\n\n/**\n * Parse a screen-point link. Accepts the canonical form\n * `athena-point://screenshot?x=…&y=…&w=…&h=…&tab=…` and tolerates what a\n * model plausibly writes instead: `width`/`height` for `w`/`h`, `tab_id` for\n * `tab`, decimals, a leading `~`, and an empty or different host. Rejects,\n * with a reason, anything that would place a marker in the wrong spot:\n * non-finite or negative numbers, a zero or negative basis, a point outside\n * its basis, and absurd extents.\n */\nexport function parseScreenPointHref(href: string | null | undefined): ScreenPointParseResult {\n if (!isScreenPointHref(href)) return { ok: false, error: \"not-screen-point\" };\n\n // `new URL` treats a non-special scheme with an empty host inconsistently\n // across engines; the query is the only part we read, so slice it off.\n const queryStart = href.indexOf(\"?\");\n if (queryStart === -1) return { ok: false, error: \"missing-coordinate\" };\n const params = new URLSearchParams(href.slice(queryStart + 1));\n\n const x = parseCoordinateNumber(params.get(\"x\"));\n const y = parseCoordinateNumber(params.get(\"y\"));\n if (!x.ok) return { ok: false, error: x.error === \"missing\" ? \"missing-coordinate\" : x.error };\n if (!y.ok) return { ok: false, error: y.error === \"missing\" ? \"missing-coordinate\" : y.error };\n\n const widthRaw = params.get(\"w\") ?? params.get(\"width\") ?? params.get(\"basisWidth\");\n const heightRaw = params.get(\"h\") ?? params.get(\"height\") ?? params.get(\"basisHeight\");\n let basis: ScreenSize | null = null;\n if (widthRaw !== null || heightRaw !== null) {\n const width = parseCoordinateNumber(widthRaw);\n const height = parseCoordinateNumber(heightRaw);\n // One dimension without the other is not a basis.\n if (!width.ok || !height.ok) {\n const failed = !width.ok ? width : height;\n if (failed.ok) return { ok: false, error: \"degenerate-basis\" };\n return {\n ok: false,\n error: failed.error === \"missing\" ? \"degenerate-basis\" : failed.error,\n };\n }\n if (width.value <= 0 || height.value <= 0) return { ok: false, error: \"degenerate-basis\" };\n basis = { width: width.value, height: height.value };\n if (x.value > basis.width || y.value > basis.height) {\n return { ok: false, error: \"out-of-bounds\" };\n }\n }\n\n const tabRaw = params.get(\"tab\") ?? params.get(\"tab_id\") ?? params.get(\"tabId\");\n let tabId: number | null = null;\n if (tabRaw !== null && tabRaw.trim() !== \"\") {\n const tab = Number(tabRaw);\n tabId = Number.isInteger(tab) && tab >= 0 ? tab : null;\n }\n\n return { ok: true, point: { x: x.value, y: y.value, basis, tabId } };\n}\n\n/** The canonical link for a point — what the prompt teaches the agent to write. */\nexport function serializeScreenPointHref(point: ScreenPoint): string {\n const params = new URLSearchParams();\n params.set(\"x\", String(point.x));\n params.set(\"y\", String(point.y));\n if (point.basis) {\n params.set(\"w\", String(point.basis.width));\n params.set(\"h\", String(point.basis.height));\n }\n if (point.tabId !== null) params.set(\"tab\", String(point.tabId));\n return `${SCREEN_POINT_HREF_PREFIX}${SCREEN_POINT_HOST}?${params.toString()}`;\n}\n\n/**\n * Where the point sits as a fraction of its basis, `0..1` on each axis, or\n * `null` when the point has no basis to be relative to. This is the only\n * number a renderer needs: `left: ${fx * 100}%` places the marker on any\n * rendition of the screenshot, whatever size it is drawn at.\n */\nexport function screenPointFraction(point: ScreenPoint): { fx: number; fy: number } | null {\n if (!point.basis) return null;\n return { fx: point.x / point.basis.width, fy: point.y / point.basis.height };\n}\n\n/** Whether two sizes are the same to within the few-pixel jitter a resize or scrollbar introduces. */\nexport function sameScreenSize({\n a,\n b,\n tolerancePx = 4,\n}: {\n a: ScreenSize;\n b: ScreenSize;\n tolerancePx?: number;\n}): boolean {\n return Math.abs(a.width - b.width) <= tolerancePx && Math.abs(a.height - b.height) <= tolerancePx;\n}\n\n/**\n * How a point's basis relates to the screenshot it is about to be drawn on.\n * Mirrors the runtime's dispatch contract (`browser_coordinate_contract.json`):\n * the same size or a few pixels of jitter is `identity`; a uniform ratio on\n * both axes (browser zoom, a resized window at the same aspect) is\n * `uniform-scale` and the fraction still lands on the same element; anything\n * else is a `mismatch` — a different aspect ratio means the screenshot shows a\n * different framing (chrome headroom, a rotated device, a different tab) and\n * a fraction placed on it would point at the wrong thing.\n */\nexport type ScreenBasisFit =\n | { kind: \"identity\" }\n | { kind: \"uniform-scale\"; scale: number }\n | { kind: \"mismatch\"; aspectDelta: number };\n\nexport function fitScreenBasis({\n basis,\n target,\n aspectTolerance = 0.02,\n}: {\n basis: ScreenSize;\n target: ScreenSize;\n aspectTolerance?: number;\n}): ScreenBasisFit {\n if (\n basis.width <= 0 ||\n basis.height <= 0 ||\n target.width <= 0 ||\n target.height <= 0 ||\n ![basis.width, basis.height, target.width, target.height].every(Number.isFinite)\n ) {\n return { kind: \"mismatch\", aspectDelta: Number.POSITIVE_INFINITY };\n }\n if (sameScreenSize({ a: basis, b: target })) return { kind: \"identity\" };\n const scaleX = target.width / basis.width;\n const scaleY = target.height / basis.height;\n const aspectDelta = Math.abs(scaleX - scaleY) / Math.max(scaleX, scaleY);\n if (aspectDelta <= aspectTolerance) return { kind: \"uniform-scale\", scale: (scaleX + scaleY) / 2 };\n return { kind: \"mismatch\", aspectDelta };\n}\n\n/** Human-readable point for tooltips and accessible names: `(1038, 81) in a 1280×720 screenshot`. */\nexport function formatScreenPoint(point: ScreenPoint): string {\n const pair = `(${point.x}, ${point.y})`;\n if (!point.basis) return `${pair} — screenshot size not recorded`;\n return `${pair} in a ${point.basis.width}×${point.basis.height} screenshot`;\n}\n\n/** The sentence a surface shows for a link that failed to parse. */\nexport function describeScreenPointError(error: ScreenPointParseError): string {\n switch (error) {\n case \"not-screen-point\":\n return \"Not a screen point\";\n case \"missing-coordinate\":\n return \"Screen point is missing x or y\";\n case \"non-finite-number\":\n return \"Screen point has a non-numeric value\";\n case \"negative-number\":\n return \"Screen point has a negative value\";\n case \"degenerate-basis\":\n return \"Screen point has an invalid screenshot size\";\n case \"out-of-bounds\":\n return \"Screen point lies outside its screenshot\";\n case \"extent-too-large\":\n return \"Screen point value is implausibly large\";\n }\n}\n\n/**\n * Screen points written into a text block, in order. Used by surfaces that\n * work on the raw markdown (the static message body's node parser, the\n * extension's plain-text composer) and by tests. Only the link form is\n * recognised — a bare `(1038, 81)` in prose is text, never a point, so a\n * model that was not taught the scheme changes nothing.\n */\nexport function findScreenPointLinks(\n text: string,\n): Array<{ start: number; end: number; label: string; href: string }> {\n const results: Array<{ start: number; end: number; label: string; href: string }> = [];\n const pattern = /\\[([^\\]\\n]*)\\]\\((athena-point:\\/\\/[^\\s)]*)\\)/g;\n for (const match of text.matchAll(pattern)) {\n if (match.index === undefined) continue;\n results.push({\n start: match.index,\n end: match.index + match[0].length,\n label: match[1],\n href: match[2],\n });\n }\n return results;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"screen-point.js","sources":["../../../../../agent-runtime/src/screen-point.ts"],"sourcesContent":["/**\n * Screen points: the agent's on-screen coordinates as structured, renderable\n * data.\n *\n * Browser agents already speak in `[x, y]` pairs — the `coordinate` argument\n * of `chrome_computer` / `device_computer` / `computer` is a pixel in the\n * screenshot the model was shown. In prose those pairs arrive as free text\n * (\"Delete at ~(1038, 81)\"), which no surface can render as anything but\n * characters. A screen point is that same pair written as a markdown link\n * whose destination carries the pair *and the dimensions of the screenshot it\n * was read from*:\n *\n * [Delete](athena-point://screenshot?x=1038&y=81&w=1280&h=720&tab=8811)\n *\n * The agent keeps its native vocabulary (the coordinate it already has, the\n * screenshot size it was told) and does no arithmetic; every chat surface\n * that understands the scheme renders the link as a chip and, because the\n * basis dimensions travel with the point, can place a marker at the right\n * relative spot on any later rendition of that screenshot however the\n * transcript is laid out or the user's window is resized. Surfaces that do\n * not understand the scheme show the label as plain text (a link with an\n * unknown protocol), which is exactly the prose we have today — the format\n * degrades to the status quo, never below it.\n *\n * Pure string and number work, UI-library-free, shared by every chat surface\n * (Olympus, the React SDK hosts, the Chrome extension side panel) so a point\n * that is valid on one surface is valid on all of them.\n */\n\n/** URL scheme of a screen-point link. */\nexport const SCREEN_POINT_PROTOCOL = \"athena-point:\";\n/** Host of a screen-point link: the coordinate space the pair lives in. */\nexport const SCREEN_POINT_HOST = \"screenshot\";\n/** `href.startsWith(SCREEN_POINT_HREF_PREFIX)` is the cheap pre-check every renderer runs. */\nexport const SCREEN_POINT_HREF_PREFIX = `${SCREEN_POINT_PROTOCOL}//`;\n/**\n * The largest coordinate or dimension a point may carry. No screenshot the\n * runtime delivers approaches this; anything larger is a typo or a unit mix-up.\n */\nexport const SCREEN_POINT_MAX_EXTENT = 32_767;\n/**\n * The model-facing CUA screenshot size the Chrome extension emits by default\n * (`athena-in-chrome/extension/src/background.ts`, `CHROME_DEFAULT_WIDTH` /\n * `CHROME_DEFAULT_HEIGHT` in `chrome_browser_tools.py`). Surfaces may offer\n * it as the fallback basis for a point that omits `w`/`h`, clearly labelled\n * as assumed.\n */\nexport const DEFAULT_CUA_SCREENSHOT_SIZE = { width: 1280, height: 720 } as const;\n\n/** Width and height of a coordinate space, in that space's pixels. */\nexport interface ScreenSize {\n width: number;\n height: number;\n}\n\n/**\n * A point in screenshot space. `basis` is the size of the screenshot the pair\n * was read from; `null` when the link omitted it, in which case the point can\n * be shown but not placed. `tabId` is the Chrome tab the screenshot came from\n * when the agent had one.\n */\nexport interface ScreenPoint {\n x: number;\n y: number;\n basis: ScreenSize | null;\n tabId: number | null;\n}\n\n/**\n * Why a link that starts with the scheme could not become a point. Rendered\n * surfaces show the label as text with the reason in a tooltip, so a broken\n * link never becomes an interactive marker that points nowhere.\n */\nexport type ScreenPointParseError =\n | \"not-screen-point\"\n | \"missing-coordinate\"\n | \"non-finite-number\"\n | \"negative-number\"\n | \"degenerate-basis\"\n | \"out-of-bounds\"\n | \"extent-too-large\";\n\nexport type ScreenPointParseResult =\n | { ok: true; point: ScreenPoint }\n | { ok: false; error: ScreenPointParseError };\n\n/** A number the agent may have written with a decimal point or a `~`: keep the integer pixel. */\nconst NUMBER_PATTERN = /^~?\\s*(-?\\d+(?:\\.\\d+)?)$/;\n\ntype NumberParse =\n | { ok: true; value: number }\n | { ok: false; error: \"non-finite-number\" | \"negative-number\" | \"extent-too-large\" }\n | { ok: false; error: \"missing\" };\n\nfunction parseCoordinateNumber(raw: string | null): NumberParse {\n if (raw === null || raw.trim() === \"\") return { ok: false, error: \"missing\" };\n const match = NUMBER_PATTERN.exec(raw.trim());\n if (!match) return { ok: false, error: \"non-finite-number\" };\n const value = Number(match[1]);\n if (!Number.isFinite(value)) return { ok: false, error: \"non-finite-number\" };\n if (value < 0) return { ok: false, error: \"negative-number\" };\n if (value > SCREEN_POINT_MAX_EXTENT) return { ok: false, error: \"extent-too-large\" };\n return { ok: true, value: Math.round(value) };\n}\n\n/** Whether `href` is a screen-point link at all (the cheap pre-check). */\nexport function isScreenPointHref(href: string | null | undefined): href is string {\n return typeof href === \"string\" && href.startsWith(SCREEN_POINT_HREF_PREFIX);\n}\n\n/**\n * Parse a screen-point link. Accepts the canonical form\n * `athena-point://screenshot?x=…&y=…&w=…&h=…&tab=…` and tolerates what a\n * model plausibly writes instead: `width`/`height` for `w`/`h`, `tab_id` for\n * `tab`, decimals, a leading `~`, and an empty or different host. Rejects,\n * with a reason, anything that would place a marker in the wrong spot:\n * non-finite or negative numbers, a zero or negative basis, a point outside\n * its basis, and absurd extents.\n */\nexport function parseScreenPointHref(href: string | null | undefined): ScreenPointParseResult {\n if (!isScreenPointHref(href)) return { ok: false, error: \"not-screen-point\" };\n\n // `new URL` treats a non-special scheme with an empty host inconsistently\n // across engines; the query is the only part we read, so slice it off.\n const queryStart = href.indexOf(\"?\");\n if (queryStart === -1) return { ok: false, error: \"missing-coordinate\" };\n const params = new URLSearchParams(href.slice(queryStart + 1));\n\n const x = parseCoordinateNumber(params.get(\"x\"));\n const y = parseCoordinateNumber(params.get(\"y\"));\n if (!x.ok) return { ok: false, error: x.error === \"missing\" ? \"missing-coordinate\" : x.error };\n if (!y.ok) return { ok: false, error: y.error === \"missing\" ? \"missing-coordinate\" : y.error };\n\n const widthRaw = params.get(\"w\") ?? params.get(\"width\") ?? params.get(\"basisWidth\");\n const heightRaw = params.get(\"h\") ?? params.get(\"height\") ?? params.get(\"basisHeight\");\n let basis: ScreenSize | null = null;\n if (widthRaw !== null || heightRaw !== null) {\n const width = parseCoordinateNumber(widthRaw);\n const height = parseCoordinateNumber(heightRaw);\n // One dimension without the other is not a basis.\n if (!width.ok || !height.ok) {\n const failed = !width.ok ? width : height;\n if (failed.ok) return { ok: false, error: \"degenerate-basis\" };\n return {\n ok: false,\n error: failed.error === \"missing\" ? \"degenerate-basis\" : failed.error,\n };\n }\n if (width.value <= 0 || height.value <= 0) return { ok: false, error: \"degenerate-basis\" };\n basis = { width: width.value, height: height.value };\n if (x.value > basis.width || y.value > basis.height) {\n return { ok: false, error: \"out-of-bounds\" };\n }\n }\n\n const tabRaw = params.get(\"tab\") ?? params.get(\"tab_id\") ?? params.get(\"tabId\");\n let tabId: number | null = null;\n if (tabRaw !== null && tabRaw.trim() !== \"\") {\n const tab = Number(tabRaw);\n tabId = Number.isInteger(tab) && tab >= 0 ? tab : null;\n }\n\n return { ok: true, point: { x: x.value, y: y.value, basis, tabId } };\n}\n\n/** The canonical link for a point — what the prompt teaches the agent to write. */\nexport function serializeScreenPointHref(point: ScreenPoint): string {\n const params = new URLSearchParams();\n params.set(\"x\", String(point.x));\n params.set(\"y\", String(point.y));\n if (point.basis) {\n params.set(\"w\", String(point.basis.width));\n params.set(\"h\", String(point.basis.height));\n }\n if (point.tabId !== null) params.set(\"tab\", String(point.tabId));\n return `${SCREEN_POINT_HREF_PREFIX}${SCREEN_POINT_HOST}?${params.toString()}`;\n}\n\n/**\n * Where the point sits as a fraction of its basis, `0..1` on each axis, or\n * `null` when the point has no basis to be relative to. This is the only\n * number a renderer needs: `left: ${fx * 100}%` places the marker on any\n * rendition of the screenshot, whatever size it is drawn at.\n */\nexport function screenPointFraction(point: ScreenPoint): { fx: number; fy: number } | null {\n if (!point.basis) return null;\n return { fx: point.x / point.basis.width, fy: point.y / point.basis.height };\n}\n\n/** Whether two sizes are the same to within the few-pixel jitter a resize or scrollbar introduces. */\nexport function sameScreenSize({\n a,\n b,\n tolerancePx = 4,\n}: {\n a: ScreenSize;\n b: ScreenSize;\n tolerancePx?: number;\n}): boolean {\n return Math.abs(a.width - b.width) <= tolerancePx && Math.abs(a.height - b.height) <= tolerancePx;\n}\n\n/**\n * How a point's basis relates to the screenshot it is about to be drawn on.\n * Mirrors the runtime's dispatch contract (`browser_coordinate_contract.json`):\n * the same size or a few pixels of jitter is `identity`; a uniform ratio on\n * both axes (browser zoom, a resized window at the same aspect) is\n * `uniform-scale` and the fraction still lands on the same element; anything\n * else is a `mismatch` — a different aspect ratio means the screenshot shows a\n * different framing (chrome headroom, a rotated device, a different tab) and\n * a fraction placed on it would point at the wrong thing.\n */\nexport type ScreenBasisFit =\n | { kind: \"identity\" }\n | { kind: \"uniform-scale\"; scale: number }\n | { kind: \"mismatch\"; aspectDelta: number };\n\nexport function fitScreenBasis({\n basis,\n target,\n aspectTolerance = 0.02,\n}: {\n basis: ScreenSize;\n target: ScreenSize;\n aspectTolerance?: number;\n}): ScreenBasisFit {\n if (\n basis.width <= 0 ||\n basis.height <= 0 ||\n target.width <= 0 ||\n target.height <= 0 ||\n ![basis.width, basis.height, target.width, target.height].every(Number.isFinite)\n ) {\n return { kind: \"mismatch\", aspectDelta: Number.POSITIVE_INFINITY };\n }\n if (sameScreenSize({ a: basis, b: target })) return { kind: \"identity\" };\n const scaleX = target.width / basis.width;\n const scaleY = target.height / basis.height;\n const aspectDelta = Math.abs(scaleX - scaleY) / Math.max(scaleX, scaleY);\n if (aspectDelta <= aspectTolerance) return { kind: \"uniform-scale\", scale: (scaleX + scaleY) / 2 };\n return { kind: \"mismatch\", aspectDelta };\n}\n\n/** Human-readable point for tooltips and accessible names: `(1038, 81) in a 1280×720 screenshot`. */\nexport function formatScreenPoint(point: ScreenPoint): string {\n const pair = `(${point.x}, ${point.y})`;\n if (!point.basis) return `${pair} — screenshot size not recorded`;\n return `${pair} in a ${point.basis.width}×${point.basis.height} screenshot`;\n}\n\n/** The sentence a surface shows for a link that failed to parse. */\nexport function describeScreenPointError(error: ScreenPointParseError): string {\n switch (error) {\n case \"not-screen-point\":\n return \"Not a screen point\";\n case \"missing-coordinate\":\n return \"Screen point is missing x or y\";\n case \"non-finite-number\":\n return \"Screen point has a non-numeric value\";\n case \"negative-number\":\n return \"Screen point has a negative value\";\n case \"degenerate-basis\":\n return \"Screen point has an invalid screenshot size\";\n case \"out-of-bounds\":\n return \"Screen point lies outside its screenshot\";\n case \"extent-too-large\":\n return \"Screen point value is implausibly large\";\n }\n}\n\n/**\n * Screen points written into a text block, in order. Used by surfaces that\n * work on the raw markdown (the static message body's node parser, the\n * extension's plain-text composer) and by tests. Only the link form is\n * recognised — a bare `(1038, 81)` in prose is text, never a point, so a\n * model that was not taught the scheme changes nothing.\n */\nexport function findScreenPointLinks(\n text: string,\n): Array<{ start: number; end: number; label: string; href: string }> {\n const results: Array<{ start: number; end: number; label: string; href: string }> = [];\n const pattern = /\\[([^\\]\\n]*)\\]\\((athena-point:\\/\\/[^\\s)]*)\\)/g;\n for (const match of text.matchAll(pattern)) {\n if (match.index === undefined) continue;\n results.push({\n start: match.index,\n end: match.index + match[0].length,\n label: match[1],\n href: match[2],\n });\n }\n return results;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"send-recovery.cjs","sources":["../../../../../agent-runtime/src/send-recovery.ts"],"sourcesContent":["import type {\n StatewireDiagnosticsCommandUpdate,\n StatewireDiagnosticsRecorder,\n} from './statewire-diagnostics';\nimport { isDeepAgentStateHydrated } from './statewire-state';\nimport type { StatewireFailedSendEntry, StatewireFailedSendLane } from './thread-extras';\n\n/**\n * Transport-level send durability for the deep-agent thread (the successor to\n * Olympus' localStorage outbox, #29368 → #31504).\n *\n * The durable journal itself is the statewire session record: every command is\n * persisted through the mounted session storage from `send()` until the server\n * durably admits or judges it, and a reload resends survivors on the SAME lane\n * seq (exactly-once — the server absorbs replayed seqs at or below its\n * watermark). What the record cannot recover on its own is a send the client\n * had to settle without a server verdict — a proven session loss or a record\n * reset settles it `lost`/`aborted` and RETAINS it in the record's failed lane\n * (message-carrying sends only), where the vendor drops it as soon as a\n * canonical snapshot shows the message landed (`failureCovered`).\n *\n * This controller turns that persisted failed lane back into live sends: once\n * the thread snapshot has hydrated (so covered entries are already gone), every\n * failed send the server NEVER judged is re-dispatched through the thread's own\n * `retryFailed` — same client-minted message id, re-anchored against the\n * current thread — which the host dedupes by id (a still-queued id re-places,\n * a durable one rejects `duplicate-id`). Server-judged fates (`rejected`,\n * `failed`, `crashed`, `expired`) are the user's decision and stay in the\n * manual retry/discard affordance.\n *\n * Ordering: an `aborted` send stamped `predecessorLost` was never attempted\n * because an earlier send of the same batch WAS attempted and settled `lost`\n * — it may or may not have run. Re-dispatching the successor first could land\n * it ahead of a predecessor that later turns out to have landed too, so the\n * successor is held until every preceding `lost` predecessor has settled: it\n * was covered by a snapshot, its retry was judged by the server, or the user\n * discarded it.\n */\n\n/** Fates the server never saw: the send died client-side (unmount, stream\n * gone, session lost) and is safe to re-dispatch under its original message\n * id. The complement — rejected/failed/crashed/expired — is server-judged. */\nconst AUTO_RETRY_FATES: ReadonlySet<string> = new Set(['lost', 'aborted']);\n\n/** A failed send is re-dispatched at most this many times per attach; a\n * persistent failure (repeated session losses) stays in the manual lane. */\nconst MAX_AUTO_RETRIES_PER_MESSAGE = 3;\n\n/** Persisted failures older than this stay manual: auto-sending a message the\n * user abandoned a day ago is worse than surfacing it for a decision. Mirrors\n * the retired outbox's retention window. */\nconst MAX_AUTO_RETRY_AGE_MS = 24 * 60 * 60 * 1000;\n\nexport interface DeepAgentSendRecoveryOptions {\n /**\n * Reads the thread's failed-send lane. `null` while it is unavailable (the\n * client has not mounted yet, or its extras are not a statewire thread) —\n * the controller stays armed and retries on the next state change.\n */\n lane: () => StatewireFailedSendLane | null;\n /**\n * Persisted failure stamps (message id → epoch ms of the original send) for\n * the freshness gate. `null`/absent means no persistence is readable — every\n * lane entry then counts as fresh (an in-memory failed lane can only hold\n * failures from this page session).\n */\n failureStamps?: (() => ReadonlyMap<string, number> | null) | undefined;\n /** Transport lifecycle recorder; read per event so a late-mounted recorder still sees retries. */\n diagnostics?: (() => StatewireDiagnosticsRecorder | undefined) | undefined;\n /** Injectable clock (tests). */\n now?: () => number;\n /** Sweep scheduler; defaults to `queueMicrotask` so a sweep never dispatches\n * from inside a statewire flush or a React render. */\n schedule?: (task: () => void) => void;\n}\n\nexport interface DeepAgentSendRecovery {\n /** Feed every (normalized) statewire state change; hydration arms the mount sweep. */\n observeStateChange(state: unknown): void;\n /** Feed every command-lifecycle update; a never-server-judged settle arms a sweep. */\n observeCommand(update: StatewireDiagnosticsCommandUpdate): void;\n /** Feed client notifications too: failed history can publish after the\n * command settles, without any change to the canonical thread snapshot. */\n observeFailedLaneChange(): void;\n dispose(): void;\n}\n\n/** Whether a settle failure is a client-side death the recovery re-dispatches\n * (the server never judged the command). Exported for host-side telemetry. */\nexport function shouldAutoRetryFailedSend(failure: { fate: string } | undefined): boolean {\n return failure !== undefined && AUTO_RETRY_FATES.has(failure.fate);\n}\n\n/** Whether a failed entry must wait for a lost predecessor to settle first. */\nfunction awaitsLostPredecessor(entry: StatewireFailedSendEntry): boolean {\n return entry.sendFailure.fate === 'aborted' && entry.sendFailure.predecessorLost === true;\n}\n\n/** The client-minted message id a run send carries, off a command update's params. */\nfunction commandMessageId(update: StatewireDiagnosticsCommandUpdate): string | null {\n const first = update.params?.[0] as { message?: { id?: unknown } } | undefined;\n const id = first?.message?.id;\n return typeof id === 'string' ? id : null;\n}\n\nexport function createDeepAgentSendRecovery(\n options: DeepAgentSendRecoveryOptions\n): DeepAgentSendRecovery {\n const now = options.now ?? Date.now;\n const schedule = options.schedule ?? ((task: () => void) => queueMicrotask(task));\n\n let disposed = false;\n let hydrated = false;\n /** Work may exist: set at hydration and on retryable settles, cleared by a\n * sweep that could actually read the lane. */\n let armed = false;\n let sweepScheduled = false;\n /** Retry budget per message id, for this controller's attach lifetime. */\n const attempts = new Map<string, number>();\n /** Message ids already diagnosed as stale/exhausted, so the lane re-scan\n * stays quiet about them. */\n const surfacedSkips = new Set<string>();\n /** Message ids whose deferral behind a lost predecessor was diagnosed once. */\n const surfacedDeferrals = new Set<string>();\n /** Re-dispatched sends still awaiting the server's verdict: a successor\n * stamped `predecessorLost` waits while any of these is pending. */\n const inFlight = new Set<string>();\n /** Successors held back by the last sweep; a non-empty set keeps the\n * controller armed so the next settle or state change re-sweeps. */\n const deferred = new Set<string>();\n\n const record = (event: string, data?: Record<string, unknown>): void => {\n options.diagnostics?.()?.record(event, data);\n };\n\n const sweep = (): void => {\n sweepScheduled = false;\n if (disposed || !hydrated || !armed) return;\n const lane = options.lane();\n // No readable lane yet: stay armed so the next state change re-sweeps.\n if (lane === null) return;\n armed = false;\n deferred.clear();\n const stamps = options.failureStamps?.() ?? null;\n // A `lost` entry earlier in lane order that this sweep leaves unsettled\n // (skipped as stale/exhausted, or re-dispatched and now in flight).\n let lostPredecessorPending = false;\n for (const entry of lane.failed) {\n if (!shouldAutoRetryFailedSend(entry.sendFailure)) continue;\n // Shared retry/discard may still be waiting for the storage lock. The\n // visible lane can keep this entry until then; do not spend another\n // attempt on a send whose previous retry is still awaiting a verdict.\n if (inFlight.has(entry.id)) {\n if (entry.sendFailure.fate === 'lost') lostPredecessorPending = true;\n continue;\n }\n if (awaitsLostPredecessor(entry) && (lostPredecessorPending || inFlight.size > 0)) {\n deferred.add(entry.id);\n if (!surfacedDeferrals.has(entry.id)) {\n surfacedDeferrals.add(entry.id);\n record('send-recovery/deferred', { messageId: entry.id });\n }\n continue;\n }\n const isLost = entry.sendFailure.fate === 'lost';\n const stamp = stamps?.get(entry.id);\n if (stamp !== undefined && now() - stamp > MAX_AUTO_RETRY_AGE_MS) {\n if (!surfacedSkips.has(entry.id)) {\n surfacedSkips.add(entry.id);\n record('send-recovery/stale', { messageId: entry.id, ageMs: Math.round(now() - stamp) });\n }\n if (isLost) lostPredecessorPending = true;\n continue;\n }\n const attempt = (attempts.get(entry.id) ?? 0) + 1;\n if (attempt > MAX_AUTO_RETRIES_PER_MESSAGE) {\n if (!surfacedSkips.has(entry.id)) {\n surfacedSkips.add(entry.id);\n record('send-recovery/exhausted', { messageId: entry.id });\n }\n if (isLost) lostPredecessorPending = true;\n continue;\n }\n try {\n lane.retryFailed(entry.id);\n attempts.set(entry.id, attempt);\n inFlight.add(entry.id);\n if (isLost) lostPredecessorPending = true;\n record('send-recovery/retry', {\n messageId: entry.id,\n fate: entry.sendFailure.fate,\n attempt,\n });\n } catch {\n // The entry settled or was covered between the lane read and the\n // retry (the vendor drops covered failures against every snapshot);\n // nothing was dispatched.\n record('send-recovery/raced', { messageId: entry.id });\n }\n }\n // Held successors must be revisited once their predecessors settle.\n if (deferred.size > 0) armed = true;\n };\n\n const scheduleSweep = (): void => {\n if (disposed || sweepScheduled || !hydrated || !armed) return;\n sweepScheduled = true;\n schedule(sweep);\n };\n\n return {\n observeStateChange(state) {\n if (disposed) return;\n if (!hydrated && isDeepAgentStateHydrated(state)) {\n // First real snapshot: the vendor's failureCovered pass has had its\n // chance to drop entries whose message actually landed, so whatever\n // remains provably never reached the thread.\n hydrated = true;\n armed = true;\n }\n scheduleSweep();\n },\n observeCommand(update) {\n if (disposed || update.status !== 'settled') return;\n const messageId = commandMessageId(update);\n if (messageId !== null) inFlight.delete(messageId);\n // A never-server-judged settle means new work; any settle while\n // successors are held may be the predecessor they wait on.\n if (!shouldAutoRetryFailedSend(update.failure) && deferred.size === 0) return;\n armed = true;\n scheduleSweep();\n },\n observeFailedLaneChange() {\n if (disposed) return;\n armed = true;\n scheduleSweep();\n },\n dispose() {\n disposed = true;\n },\n };\n}\n"],"names":["isDeepAgentStateHydrated"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"send-recovery.js","sources":["../../../../../agent-runtime/src/send-recovery.ts"],"sourcesContent":["import type {\n StatewireDiagnosticsCommandUpdate,\n StatewireDiagnosticsRecorder,\n} from './statewire-diagnostics';\nimport { isDeepAgentStateHydrated } from './statewire-state';\nimport type { StatewireFailedSendEntry, StatewireFailedSendLane } from './thread-extras';\n\n/**\n * Transport-level send durability for the deep-agent thread (the successor to\n * Olympus' localStorage outbox, #29368 → #31504).\n *\n * The durable journal itself is the statewire session record: every command is\n * persisted through the mounted session storage from `send()` until the server\n * durably admits or judges it, and a reload resends survivors on the SAME lane\n * seq (exactly-once — the server absorbs replayed seqs at or below its\n * watermark). What the record cannot recover on its own is a send the client\n * had to settle without a server verdict — a proven session loss or a record\n * reset settles it `lost`/`aborted` and RETAINS it in the record's failed lane\n * (message-carrying sends only), where the vendor drops it as soon as a\n * canonical snapshot shows the message landed (`failureCovered`).\n *\n * This controller turns that persisted failed lane back into live sends: once\n * the thread snapshot has hydrated (so covered entries are already gone), every\n * failed send the server NEVER judged is re-dispatched through the thread's own\n * `retryFailed` — same client-minted message id, re-anchored against the\n * current thread — which the host dedupes by id (a still-queued id re-places,\n * a durable one rejects `duplicate-id`). Server-judged fates (`rejected`,\n * `failed`, `crashed`, `expired`) are the user's decision and stay in the\n * manual retry/discard affordance.\n *\n * Ordering: an `aborted` send stamped `predecessorLost` was never attempted\n * because an earlier send of the same batch WAS attempted and settled `lost`\n * — it may or may not have run. Re-dispatching the successor first could land\n * it ahead of a predecessor that later turns out to have landed too, so the\n * successor is held until every preceding `lost` predecessor has settled: it\n * was covered by a snapshot, its retry was judged by the server, or the user\n * discarded it.\n */\n\n/** Fates the server never saw: the send died client-side (unmount, stream\n * gone, session lost) and is safe to re-dispatch under its original message\n * id. The complement — rejected/failed/crashed/expired — is server-judged. */\nconst AUTO_RETRY_FATES: ReadonlySet<string> = new Set(['lost', 'aborted']);\n\n/** A failed send is re-dispatched at most this many times per attach; a\n * persistent failure (repeated session losses) stays in the manual lane. */\nconst MAX_AUTO_RETRIES_PER_MESSAGE = 3;\n\n/** Persisted failures older than this stay manual: auto-sending a message the\n * user abandoned a day ago is worse than surfacing it for a decision. Mirrors\n * the retired outbox's retention window. */\nconst MAX_AUTO_RETRY_AGE_MS = 24 * 60 * 60 * 1000;\n\nexport interface DeepAgentSendRecoveryOptions {\n /**\n * Reads the thread's failed-send lane. `null` while it is unavailable (the\n * client has not mounted yet, or its extras are not a statewire thread) —\n * the controller stays armed and retries on the next state change.\n */\n lane: () => StatewireFailedSendLane | null;\n /**\n * Persisted failure stamps (message id → epoch ms of the original send) for\n * the freshness gate. `null`/absent means no persistence is readable — every\n * lane entry then counts as fresh (an in-memory failed lane can only hold\n * failures from this page session).\n */\n failureStamps?: (() => ReadonlyMap<string, number> | null) | undefined;\n /** Transport lifecycle recorder; read per event so a late-mounted recorder still sees retries. */\n diagnostics?: (() => StatewireDiagnosticsRecorder | undefined) | undefined;\n /** Injectable clock (tests). */\n now?: () => number;\n /** Sweep scheduler; defaults to `queueMicrotask` so a sweep never dispatches\n * from inside a statewire flush or a React render. */\n schedule?: (task: () => void) => void;\n}\n\nexport interface DeepAgentSendRecovery {\n /** Feed every (normalized) statewire state change; hydration arms the mount sweep. */\n observeStateChange(state: unknown): void;\n /** Feed every command-lifecycle update; a never-server-judged settle arms a sweep. */\n observeCommand(update: StatewireDiagnosticsCommandUpdate): void;\n /** Feed client notifications too: failed history can publish after the\n * command settles, without any change to the canonical thread snapshot. */\n observeFailedLaneChange(): void;\n dispose(): void;\n}\n\n/** Whether a settle failure is a client-side death the recovery re-dispatches\n * (the server never judged the command). Exported for host-side telemetry. */\nexport function shouldAutoRetryFailedSend(failure: { fate: string } | undefined): boolean {\n return failure !== undefined && AUTO_RETRY_FATES.has(failure.fate);\n}\n\n/** Whether a failed entry must wait for a lost predecessor to settle first. */\nfunction awaitsLostPredecessor(entry: StatewireFailedSendEntry): boolean {\n return entry.sendFailure.fate === 'aborted' && entry.sendFailure.predecessorLost === true;\n}\n\n/** The client-minted message id a run send carries, off a command update's params. */\nfunction commandMessageId(update: StatewireDiagnosticsCommandUpdate): string | null {\n const first = update.params?.[0] as { message?: { id?: unknown } } | undefined;\n const id = first?.message?.id;\n return typeof id === 'string' ? id : null;\n}\n\nexport function createDeepAgentSendRecovery(\n options: DeepAgentSendRecoveryOptions\n): DeepAgentSendRecovery {\n const now = options.now ?? Date.now;\n const schedule = options.schedule ?? ((task: () => void) => queueMicrotask(task));\n\n let disposed = false;\n let hydrated = false;\n /** Work may exist: set at hydration and on retryable settles, cleared by a\n * sweep that could actually read the lane. */\n let armed = false;\n let sweepScheduled = false;\n /** Retry budget per message id, for this controller's attach lifetime. */\n const attempts = new Map<string, number>();\n /** Message ids already diagnosed as stale/exhausted, so the lane re-scan\n * stays quiet about them. */\n const surfacedSkips = new Set<string>();\n /** Message ids whose deferral behind a lost predecessor was diagnosed once. */\n const surfacedDeferrals = new Set<string>();\n /** Re-dispatched sends still awaiting the server's verdict: a successor\n * stamped `predecessorLost` waits while any of these is pending. */\n const inFlight = new Set<string>();\n /** Successors held back by the last sweep; a non-empty set keeps the\n * controller armed so the next settle or state change re-sweeps. */\n const deferred = new Set<string>();\n\n const record = (event: string, data?: Record<string, unknown>): void => {\n options.diagnostics?.()?.record(event, data);\n };\n\n const sweep = (): void => {\n sweepScheduled = false;\n if (disposed || !hydrated || !armed) return;\n const lane = options.lane();\n // No readable lane yet: stay armed so the next state change re-sweeps.\n if (lane === null) return;\n armed = false;\n deferred.clear();\n const stamps = options.failureStamps?.() ?? null;\n // A `lost` entry earlier in lane order that this sweep leaves unsettled\n // (skipped as stale/exhausted, or re-dispatched and now in flight).\n let lostPredecessorPending = false;\n for (const entry of lane.failed) {\n if (!shouldAutoRetryFailedSend(entry.sendFailure)) continue;\n // Shared retry/discard may still be waiting for the storage lock. The\n // visible lane can keep this entry until then; do not spend another\n // attempt on a send whose previous retry is still awaiting a verdict.\n if (inFlight.has(entry.id)) {\n if (entry.sendFailure.fate === 'lost') lostPredecessorPending = true;\n continue;\n }\n if (awaitsLostPredecessor(entry) && (lostPredecessorPending || inFlight.size > 0)) {\n deferred.add(entry.id);\n if (!surfacedDeferrals.has(entry.id)) {\n surfacedDeferrals.add(entry.id);\n record('send-recovery/deferred', { messageId: entry.id });\n }\n continue;\n }\n const isLost = entry.sendFailure.fate === 'lost';\n const stamp = stamps?.get(entry.id);\n if (stamp !== undefined && now() - stamp > MAX_AUTO_RETRY_AGE_MS) {\n if (!surfacedSkips.has(entry.id)) {\n surfacedSkips.add(entry.id);\n record('send-recovery/stale', { messageId: entry.id, ageMs: Math.round(now() - stamp) });\n }\n if (isLost) lostPredecessorPending = true;\n continue;\n }\n const attempt = (attempts.get(entry.id) ?? 0) + 1;\n if (attempt > MAX_AUTO_RETRIES_PER_MESSAGE) {\n if (!surfacedSkips.has(entry.id)) {\n surfacedSkips.add(entry.id);\n record('send-recovery/exhausted', { messageId: entry.id });\n }\n if (isLost) lostPredecessorPending = true;\n continue;\n }\n try {\n lane.retryFailed(entry.id);\n attempts.set(entry.id, attempt);\n inFlight.add(entry.id);\n if (isLost) lostPredecessorPending = true;\n record('send-recovery/retry', {\n messageId: entry.id,\n fate: entry.sendFailure.fate,\n attempt,\n });\n } catch {\n // The entry settled or was covered between the lane read and the\n // retry (the vendor drops covered failures against every snapshot);\n // nothing was dispatched.\n record('send-recovery/raced', { messageId: entry.id });\n }\n }\n // Held successors must be revisited once their predecessors settle.\n if (deferred.size > 0) armed = true;\n };\n\n const scheduleSweep = (): void => {\n if (disposed || sweepScheduled || !hydrated || !armed) return;\n sweepScheduled = true;\n schedule(sweep);\n };\n\n return {\n observeStateChange(state) {\n if (disposed) return;\n if (!hydrated && isDeepAgentStateHydrated(state)) {\n // First real snapshot: the vendor's failureCovered pass has had its\n // chance to drop entries whose message actually landed, so whatever\n // remains provably never reached the thread.\n hydrated = true;\n armed = true;\n }\n scheduleSweep();\n },\n observeCommand(update) {\n if (disposed || update.status !== 'settled') return;\n const messageId = commandMessageId(update);\n if (messageId !== null) inFlight.delete(messageId);\n // A never-server-judged settle means new work; any settle while\n // successors are held may be the predecessor they wait on.\n if (!shouldAutoRetryFailedSend(update.failure) && deferred.size === 0) return;\n armed = true;\n scheduleSweep();\n },\n observeFailedLaneChange() {\n if (disposed) return;\n armed = true;\n scheduleSweep();\n },\n dispose() {\n disposed = true;\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-attach-observer.cjs","sources":["../../../../../agent-runtime/src/statewire-attach-observer.ts"],"sourcesContent":["/**\n * Attach-path instrumentation for the deep-agent statewire transport, wired\n * through the two injection points statewire already exposes — the\n * `webSocket` constructor (`StatewireClient.WebsocketOptions`) and `fetch`\n * (`StatewireClient.HttpOptions`) — so the runtime can observe what the library\n * does not surface:\n *\n * - socket open latency (`ws_open_ms`), the hello frame's size and its\n * synchronous handler cost (`hello_bytes`, `hello_handler_ms`), and close\n * codes/reasons, for the thread-switch telemetry;\n * - a server `Retry-After` hint, honoured as a floor under the next dial.\n *\n * Retry-After today: statewire honours `retry-after` only on 429 command-frame\n * refusals (folded into the refusal `syn.retry`); its attach paths treat a 503\n * as a generic `HTTP 503` / `network` failure and back off exponentially with\n * no server input. Browsers never expose the response headers of a refused\n * WebSocket upgrade at all, so for the WS transport the hint can only ride\n * something the client can read: the close frame. The contract this module\n * reads is a close `reason` carrying `retry-after=<seconds>` (or\n * `retry-after-ms=<ms>`) — the pinboard half of that contract is a separate\n * change; until it lands the WS path simply sees no hint. The SSE attach\n * (`POST …/stream`) is a plain fetch, so its `Retry-After` header is read here\n * directly.\n *\n * This package compiles without the DOM lib (React Native honesty), so the\n * browser surface is typed structurally and resolved through `globalThis`.\n */\n\n/**\n * Who refused a wake (503/429), read off the response body against the two\n * refusal vocabularies the iris scaler also classifies data-plane 503s by\n * (`iris/scaler/src/v2AccessLog.ts`):\n *\n * - `\"placement\"` — the pinboard's own refusal, `{\"error\": …}` with one of its\n * known strings (\"no live workers for hostname\", \"registry cold start,\n * retry\", \"takeover in progress, retry\", …): the thread has no worker to\n * dial right now.\n * - `\"worker\"` — a pinned instance-lifecycle refusal, `{\"detail\": …}` with one\n * of its known strings (\"instance closing; retry\", \"activation queue full;\n * retry\", \"host draining; retry\", …): one instance on the worker the thread\n * is already placed on, cleared in the time a teardown takes.\n * - `\"unknown\"` — any other 503/429 body (unreadable, an unfamiliar string, a\n * real worker failure such as \"Thread history load failed\"): treated\n * conservatively — the full `Retry-After`, no dial dropped.\n * - `null` — not a refusal status.\n */\nexport type DeepAgentWakeRefusal = \"placement\" | \"worker\" | \"unknown\" | null;\n\nexport type DeepAgentAttachTimingEvent =\n | { type: \"socket-open\"; url: string; openMs: number }\n | {\n type: \"hello\";\n url: string;\n /** UTF-16 length of the hello frame — bytes for the ASCII JSON the wire carries. */\n bytes: number;\n /** Synchronous cost of statewire's message handler for the hello (parse + apply + flush). */\n handlerMs: number;\n /** Hello arrival relative to the socket open; `null` when the open was not observed. */\n sinceOpenMs: number | null;\n }\n | {\n type: \"socket-close\";\n url: string;\n code: number | undefined;\n reason: string | undefined;\n sinceOpenMs: number | null;\n }\n | {\n type: \"retry-after\";\n url: string;\n delayMs: number;\n source: \"ws-close\" | \"http-header\";\n }\n | {\n /** `POST …/prewarm` answered (see `wakeDeepAgentThread`). */\n type: \"wake\";\n url: string;\n status: number;\n durationMs: number;\n /** How the host sourced the history: already resident (`warm`) or loaded by this call. */\n history: \"fresh_load\" | \"warm\" | null;\n /** Who refused a 503/429 (see {@link DeepAgentWakeRefusal}); `null` otherwise. */\n refusal: DeepAgentWakeRefusal;\n };\n\n/** Longest server hint honoured — a buggy or hostile hint must not park a client. */\nexport const MAX_RETRY_AFTER_MS = 30_000;\n\nconst clampRetryAfter = (ms: number): number | null => {\n if (!Number.isFinite(ms)) return null;\n return Math.min(Math.max(0, Math.round(ms)), MAX_RETRY_AFTER_MS);\n};\n\n/**\n * Parse an HTTP `Retry-After` header value: delta-seconds or an HTTP-date.\n * Returns the delay in ms clamped to {@link MAX_RETRY_AFTER_MS}, or `null` when\n * the value is absent or unparseable.\n */\nexport function parseRetryAfterHeaderMs(\n value: string | null | undefined,\n now: () => number = () => Date.now(),\n): number | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n if (trimmed === \"\") return null;\n if (/^\\d+$/.test(trimmed)) return clampRetryAfter(Number(trimmed) * 1000);\n // An HTTP-date always names a weekday/month; anything else numeric-looking\n // (\"1.5\") would otherwise parse as some far-off calendar date.\n if (!/[A-Za-z]/.test(trimmed)) return null;\n const at = Date.parse(trimmed);\n if (Number.isNaN(at)) return null;\n return clampRetryAfter(at - now());\n}\n\n/**\n * Parse a retry hint off a WebSocket close reason: `retry-after=<seconds>` or\n * `retry-after-ms=<ms>` anywhere in the reason text (case-insensitive, `=` or\n * `:`). Returns ms clamped to {@link MAX_RETRY_AFTER_MS}, or `null`.\n */\nexport function parseRetryAfterFromCloseReason(\n reason: string | undefined,\n): number | null {\n if (typeof reason !== \"string\" || reason === \"\") return null;\n const match = /retry-after(-ms)?\\s*[=:]\\s*(\\d+)/i.exec(reason);\n if (!match) return null;\n const amount = Number(match[2]);\n return clampRetryAfter(match[1] ? amount : amount * 1000);\n}\n\nexport interface AttachRetryGate {\n /** Record a hint: no dial for `key` before `now + delayMs` (clamped). */\n noteRetryAfter(key: string, delayMs: number): void;\n /** Resolves once the recorded floor for `key` has passed (immediately when none). */\n waitForDial(key: string): Promise<void>;\n /** Remaining floor for `key` in ms (0 when none). */\n remainingMs(key: string): number;\n /** Drop one key's floor, or every floor. */\n reset(key?: string): void;\n}\n\n/**\n * Per-key dial floors. Keyed by the thread's attach URL so a hint delivered\n * for one thread never delays another. The floor is a minimum under\n * statewire's own backoff, never a replacement for it: the transport still\n * schedules its retry, and the dial simply waits out the remainder inside the\n * headers resolve (both attach paths resolve headers before they dial).\n */\nexport function createAttachRetryGate({\n now = () => Date.now(),\n wait = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms)),\n}: {\n now?: () => number;\n wait?: (ms: number) => Promise<void>;\n} = {}): AttachRetryGate {\n const floors = new Map<string, number>();\n const remainingMs = (key: string): number => {\n const until = floors.get(key);\n if (until === undefined) return 0;\n const remaining = until - now();\n if (remaining <= 0) {\n floors.delete(key);\n return 0;\n }\n return remaining;\n };\n return {\n noteRetryAfter(key, delayMs) {\n const clamped = clampRetryAfter(delayMs);\n if (clamped === null || clamped === 0) return;\n const until = now() + clamped;\n const existing = floors.get(key);\n if (existing === undefined || existing < until) floors.set(key, until);\n },\n async waitForDial(key) {\n // A hint that arrives while the dial sleeps extends the floor; re-check\n // after every wait rather than trusting the deadline the sleep began with.\n for (\n let remaining = remainingMs(key);\n remaining > 0;\n remaining = remainingMs(key)\n ) {\n await wait(remaining);\n }\n },\n remainingMs,\n reset(key) {\n if (key === undefined) floors.clear();\n else floors.delete(key);\n },\n };\n}\n\n/** Process-wide gate shared by every deep-agent transport in the host. */\nexport const attachRetryGate: AttachRetryGate = createAttachRetryGate();\n\n/** Structural subset of the browser `WebSocket` that statewire's WS transport touches. */\nexport interface AttachSocketLike {\n readonly readyState: number;\n readonly protocol: string;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code?: number; reason?: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\nexport type AttachWebSocketConstructor = new (\n url: string,\n protocols?: string | string[],\n) => AttachSocketLike;\n\nconst defaultNow = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n\n/** Statewire's keep-alive frame — never the hello. */\nconst isHeartbeatFrame = (data: unknown): boolean => {\n const text = String(data);\n return text === \"\" || text === \"{}\";\n};\n\n/**\n * Wrap a socket in a forwarding facade whose handler setters time the events\n * statewire attaches to (`onopen`, `onmessage`, `onclose`). A facade rather\n * than a subclass so the same instrumentation works over any host `WebSocket`\n * implementation, including test fakes.\n */\nexport function instrumentAttachSocket({\n socket,\n url,\n onEvent,\n now = defaultNow,\n}: {\n socket: AttachSocketLike;\n /** The thread attach URL the events are attributed to (not the socket URL). */\n url: string;\n onEvent: (event: DeepAgentAttachTimingEvent) => void;\n now?: () => number;\n}): AttachSocketLike {\n const createdAt = now();\n let openedAt: number | null = null;\n let helloSeen = false;\n const emit = (event: DeepAgentAttachTimingEvent): void => {\n try {\n onEvent(event);\n } catch {\n // Observers are best-effort; the wire must never fail on telemetry.\n }\n };\n let openHandler: AttachSocketLike[\"onopen\"] = null;\n let messageHandler: AttachSocketLike[\"onmessage\"] = null;\n let closeHandler: AttachSocketLike[\"onclose\"] = null;\n socket.onopen = (event) => {\n openedAt = now();\n emit({ type: \"socket-open\", url, openMs: openedAt - createdAt });\n openHandler?.(event);\n };\n socket.onmessage = (event) => {\n if (helloSeen || isHeartbeatFrame(event.data)) {\n messageHandler?.(event);\n return;\n }\n helloSeen = true;\n const startedAt = now();\n try {\n messageHandler?.(event);\n } finally {\n const finishedAt = now();\n emit({\n type: \"hello\",\n url,\n bytes: String(event.data).length,\n handlerMs: finishedAt - startedAt,\n sinceOpenMs: openedAt === null ? null : startedAt - openedAt,\n });\n }\n };\n socket.onclose = (event) => {\n const closedAt = now();\n emit({\n type: \"socket-close\",\n url,\n code: event?.code,\n reason: event?.reason,\n sinceOpenMs: openedAt === null ? null : closedAt - openedAt,\n });\n const retryAfter = parseRetryAfterFromCloseReason(event?.reason);\n if (retryAfter !== null) {\n emit({\n type: \"retry-after\",\n url,\n delayMs: retryAfter,\n source: \"ws-close\",\n });\n }\n closeHandler?.(event);\n };\n return {\n get protocol() {\n return socket.protocol;\n },\n get readyState() {\n return socket.readyState;\n },\n get onopen() {\n return openHandler;\n },\n set onopen(handler) {\n openHandler = handler;\n },\n get onmessage() {\n return messageHandler;\n },\n set onmessage(handler) {\n messageHandler = handler;\n },\n get onclose() {\n return closeHandler;\n },\n set onclose(handler) {\n closeHandler = handler;\n },\n get onerror() {\n return socket.onerror;\n },\n set onerror(handler) {\n socket.onerror = handler;\n },\n send: (data) => socket.send(data),\n close: (code, reason) => socket.close(code, reason),\n };\n}\n\n/**\n * A `WebSocket`-shaped constructor that dials through `Base` and reports the\n * attach events for `url`. Constructor semantics (`new` returns the facade)\n * match what statewire's `webSocket` option expects.\n */\nexport function instrumentedWebSocketConstructor({\n Base,\n url,\n onEvent,\n now,\n}: {\n Base: AttachWebSocketConstructor;\n url: string;\n onEvent: (event: DeepAgentAttachTimingEvent) => void;\n now?: () => number;\n}): AttachWebSocketConstructor {\n function InstrumentedWebSocket(\n socketUrl: string,\n protocols?: string | string[],\n ) {\n return instrumentAttachSocket({\n socket: new Base(socketUrl, protocols),\n url,\n onEvent,\n ...(now && { now }),\n });\n }\n return InstrumentedWebSocket as unknown as AttachWebSocketConstructor;\n}\n\n/** Structural `fetch` — what statewire's SSE transport calls. */\nexport type AttachFetch = (\n input: string | URL,\n init?: unknown,\n) => Promise<{\n ok: boolean;\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\n/**\n * A `fetch` that reports a `Retry-After` header on refused attach requests\n * (the SSE `POST …/stream`) before handing the response back untouched.\n */\nexport function instrumentedAttachFetch<TFetch extends AttachFetch>({\n baseFetch,\n url,\n onEvent,\n now,\n}: {\n baseFetch: TFetch;\n url: string;\n onEvent: (event: DeepAgentAttachTimingEvent) => void;\n now?: () => number;\n}): TFetch {\n const wrapped: AttachFetch = async (input, init) => {\n const response = await baseFetch(input, init);\n if (!response.ok && (response.status === 503 || response.status === 429)) {\n const delayMs = parseRetryAfterHeaderMs(\n response.headers.get(\"retry-after\"),\n now,\n );\n if (delayMs !== null) {\n try {\n onEvent({ type: \"retry-after\", url, delayMs, source: \"http-header\" });\n } catch {\n // Observers are best-effort.\n }\n }\n }\n return response;\n };\n return wrapped as TFetch;\n}\n\n/** Structural view of the host globals the instrumentation resolves. */\ntype AttachGlobals = {\n WebSocket?: AttachWebSocketConstructor;\n fetch?: AttachFetch;\n};\n\n/**\n * The transport option overrides for one attach: an instrumented `webSocket`\n * (WS) or `fetch` (SSE) that reports attach events for `url` and feeds\n * `Retry-After` hints into the gate. Returns `{}` where the host has no such\n * global (a server render, a non-browser runtime) so the transport keeps its\n * defaults.\n */\nexport function createAttachInstrumentation({\n url,\n sse,\n onEvent,\n retryGate = attachRetryGate,\n now,\n}: {\n url: string;\n sse: boolean;\n onEvent?: ((event: DeepAgentAttachTimingEvent) => void) | undefined;\n retryGate?: AttachRetryGate;\n now?: () => number;\n}): { webSocket?: AttachWebSocketConstructor; fetch?: AttachFetch } {\n const globals = globalThis as AttachGlobals;\n const observe = (event: DeepAgentAttachTimingEvent): void => {\n if (event.type === \"retry-after\")\n retryGate.noteRetryAfter(url, event.delayMs);\n onEvent?.(event);\n };\n if (sse) {\n const baseFetch = globals.fetch;\n if (typeof baseFetch !== \"function\") return {};\n return {\n fetch: instrumentedAttachFetch({\n baseFetch: (input, init) => baseFetch(input, init),\n url,\n onEvent: observe,\n ...(now && { now }),\n }),\n };\n }\n const Base = globals.WebSocket;\n if (typeof Base !== \"function\") return {};\n return {\n webSocket: instrumentedWebSocketConstructor({\n Base,\n url,\n onEvent: observe,\n ...(now && { now }),\n }),\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-attach-observer.js","sources":["../../../../../agent-runtime/src/statewire-attach-observer.ts"],"sourcesContent":["/**\n * Attach-path instrumentation for the deep-agent statewire transport, wired\n * through the two injection points statewire already exposes — the\n * `webSocket` constructor (`StatewireClient.WebsocketOptions`) and `fetch`\n * (`StatewireClient.HttpOptions`) — so the runtime can observe what the library\n * does not surface:\n *\n * - socket open latency (`ws_open_ms`), the hello frame's size and its\n * synchronous handler cost (`hello_bytes`, `hello_handler_ms`), and close\n * codes/reasons, for the thread-switch telemetry;\n * - a server `Retry-After` hint, honoured as a floor under the next dial.\n *\n * Retry-After today: statewire honours `retry-after` only on 429 command-frame\n * refusals (folded into the refusal `syn.retry`); its attach paths treat a 503\n * as a generic `HTTP 503` / `network` failure and back off exponentially with\n * no server input. Browsers never expose the response headers of a refused\n * WebSocket upgrade at all, so for the WS transport the hint can only ride\n * something the client can read: the close frame. The contract this module\n * reads is a close `reason` carrying `retry-after=<seconds>` (or\n * `retry-after-ms=<ms>`) — the pinboard half of that contract is a separate\n * change; until it lands the WS path simply sees no hint. The SSE attach\n * (`POST …/stream`) is a plain fetch, so its `Retry-After` header is read here\n * directly.\n *\n * This package compiles without the DOM lib (React Native honesty), so the\n * browser surface is typed structurally and resolved through `globalThis`.\n */\n\n/**\n * Who refused a wake (503/429), read off the response body against the two\n * refusal vocabularies the iris scaler also classifies data-plane 503s by\n * (`iris/scaler/src/v2AccessLog.ts`):\n *\n * - `\"placement\"` — the pinboard's own refusal, `{\"error\": …}` with one of its\n * known strings (\"no live workers for hostname\", \"registry cold start,\n * retry\", \"takeover in progress, retry\", …): the thread has no worker to\n * dial right now.\n * - `\"worker\"` — a pinned instance-lifecycle refusal, `{\"detail\": …}` with one\n * of its known strings (\"instance closing; retry\", \"activation queue full;\n * retry\", \"host draining; retry\", …): one instance on the worker the thread\n * is already placed on, cleared in the time a teardown takes.\n * - `\"unknown\"` — any other 503/429 body (unreadable, an unfamiliar string, a\n * real worker failure such as \"Thread history load failed\"): treated\n * conservatively — the full `Retry-After`, no dial dropped.\n * - `null` — not a refusal status.\n */\nexport type DeepAgentWakeRefusal = \"placement\" | \"worker\" | \"unknown\" | null;\n\nexport type DeepAgentAttachTimingEvent =\n | { type: \"socket-open\"; url: string; openMs: number }\n | {\n type: \"hello\";\n url: string;\n /** UTF-16 length of the hello frame — bytes for the ASCII JSON the wire carries. */\n bytes: number;\n /** Synchronous cost of statewire's message handler for the hello (parse + apply + flush). */\n handlerMs: number;\n /** Hello arrival relative to the socket open; `null` when the open was not observed. */\n sinceOpenMs: number | null;\n }\n | {\n type: \"socket-close\";\n url: string;\n code: number | undefined;\n reason: string | undefined;\n sinceOpenMs: number | null;\n }\n | {\n type: \"retry-after\";\n url: string;\n delayMs: number;\n source: \"ws-close\" | \"http-header\";\n }\n | {\n /** `POST …/prewarm` answered (see `wakeDeepAgentThread`). */\n type: \"wake\";\n url: string;\n status: number;\n durationMs: number;\n /** How the host sourced the history: already resident (`warm`) or loaded by this call. */\n history: \"fresh_load\" | \"warm\" | null;\n /** Who refused a 503/429 (see {@link DeepAgentWakeRefusal}); `null` otherwise. */\n refusal: DeepAgentWakeRefusal;\n };\n\n/** Longest server hint honoured — a buggy or hostile hint must not park a client. */\nexport const MAX_RETRY_AFTER_MS = 30_000;\n\nconst clampRetryAfter = (ms: number): number | null => {\n if (!Number.isFinite(ms)) return null;\n return Math.min(Math.max(0, Math.round(ms)), MAX_RETRY_AFTER_MS);\n};\n\n/**\n * Parse an HTTP `Retry-After` header value: delta-seconds or an HTTP-date.\n * Returns the delay in ms clamped to {@link MAX_RETRY_AFTER_MS}, or `null` when\n * the value is absent or unparseable.\n */\nexport function parseRetryAfterHeaderMs(\n value: string | null | undefined,\n now: () => number = () => Date.now(),\n): number | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n if (trimmed === \"\") return null;\n if (/^\\d+$/.test(trimmed)) return clampRetryAfter(Number(trimmed) * 1000);\n // An HTTP-date always names a weekday/month; anything else numeric-looking\n // (\"1.5\") would otherwise parse as some far-off calendar date.\n if (!/[A-Za-z]/.test(trimmed)) return null;\n const at = Date.parse(trimmed);\n if (Number.isNaN(at)) return null;\n return clampRetryAfter(at - now());\n}\n\n/**\n * Parse a retry hint off a WebSocket close reason: `retry-after=<seconds>` or\n * `retry-after-ms=<ms>` anywhere in the reason text (case-insensitive, `=` or\n * `:`). Returns ms clamped to {@link MAX_RETRY_AFTER_MS}, or `null`.\n */\nexport function parseRetryAfterFromCloseReason(\n reason: string | undefined,\n): number | null {\n if (typeof reason !== \"string\" || reason === \"\") return null;\n const match = /retry-after(-ms)?\\s*[=:]\\s*(\\d+)/i.exec(reason);\n if (!match) return null;\n const amount = Number(match[2]);\n return clampRetryAfter(match[1] ? amount : amount * 1000);\n}\n\nexport interface AttachRetryGate {\n /** Record a hint: no dial for `key` before `now + delayMs` (clamped). */\n noteRetryAfter(key: string, delayMs: number): void;\n /** Resolves once the recorded floor for `key` has passed (immediately when none). */\n waitForDial(key: string): Promise<void>;\n /** Remaining floor for `key` in ms (0 when none). */\n remainingMs(key: string): number;\n /** Drop one key's floor, or every floor. */\n reset(key?: string): void;\n}\n\n/**\n * Per-key dial floors. Keyed by the thread's attach URL so a hint delivered\n * for one thread never delays another. The floor is a minimum under\n * statewire's own backoff, never a replacement for it: the transport still\n * schedules its retry, and the dial simply waits out the remainder inside the\n * headers resolve (both attach paths resolve headers before they dial).\n */\nexport function createAttachRetryGate({\n now = () => Date.now(),\n wait = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms)),\n}: {\n now?: () => number;\n wait?: (ms: number) => Promise<void>;\n} = {}): AttachRetryGate {\n const floors = new Map<string, number>();\n const remainingMs = (key: string): number => {\n const until = floors.get(key);\n if (until === undefined) return 0;\n const remaining = until - now();\n if (remaining <= 0) {\n floors.delete(key);\n return 0;\n }\n return remaining;\n };\n return {\n noteRetryAfter(key, delayMs) {\n const clamped = clampRetryAfter(delayMs);\n if (clamped === null || clamped === 0) return;\n const until = now() + clamped;\n const existing = floors.get(key);\n if (existing === undefined || existing < until) floors.set(key, until);\n },\n async waitForDial(key) {\n // A hint that arrives while the dial sleeps extends the floor; re-check\n // after every wait rather than trusting the deadline the sleep began with.\n for (\n let remaining = remainingMs(key);\n remaining > 0;\n remaining = remainingMs(key)\n ) {\n await wait(remaining);\n }\n },\n remainingMs,\n reset(key) {\n if (key === undefined) floors.clear();\n else floors.delete(key);\n },\n };\n}\n\n/** Process-wide gate shared by every deep-agent transport in the host. */\nexport const attachRetryGate: AttachRetryGate = createAttachRetryGate();\n\n/** Structural subset of the browser `WebSocket` that statewire's WS transport touches. */\nexport interface AttachSocketLike {\n readonly readyState: number;\n readonly protocol: string;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code?: number; reason?: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\nexport type AttachWebSocketConstructor = new (\n url: string,\n protocols?: string | string[],\n) => AttachSocketLike;\n\nconst defaultNow = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n\n/** Statewire's keep-alive frame — never the hello. */\nconst isHeartbeatFrame = (data: unknown): boolean => {\n const text = String(data);\n return text === \"\" || text === \"{}\";\n};\n\n/**\n * Wrap a socket in a forwarding facade whose handler setters time the events\n * statewire attaches to (`onopen`, `onmessage`, `onclose`). A facade rather\n * than a subclass so the same instrumentation works over any host `WebSocket`\n * implementation, including test fakes.\n */\nexport function instrumentAttachSocket({\n socket,\n url,\n onEvent,\n now = defaultNow,\n}: {\n socket: AttachSocketLike;\n /** The thread attach URL the events are attributed to (not the socket URL). */\n url: string;\n onEvent: (event: DeepAgentAttachTimingEvent) => void;\n now?: () => number;\n}): AttachSocketLike {\n const createdAt = now();\n let openedAt: number | null = null;\n let helloSeen = false;\n const emit = (event: DeepAgentAttachTimingEvent): void => {\n try {\n onEvent(event);\n } catch {\n // Observers are best-effort; the wire must never fail on telemetry.\n }\n };\n let openHandler: AttachSocketLike[\"onopen\"] = null;\n let messageHandler: AttachSocketLike[\"onmessage\"] = null;\n let closeHandler: AttachSocketLike[\"onclose\"] = null;\n socket.onopen = (event) => {\n openedAt = now();\n emit({ type: \"socket-open\", url, openMs: openedAt - createdAt });\n openHandler?.(event);\n };\n socket.onmessage = (event) => {\n if (helloSeen || isHeartbeatFrame(event.data)) {\n messageHandler?.(event);\n return;\n }\n helloSeen = true;\n const startedAt = now();\n try {\n messageHandler?.(event);\n } finally {\n const finishedAt = now();\n emit({\n type: \"hello\",\n url,\n bytes: String(event.data).length,\n handlerMs: finishedAt - startedAt,\n sinceOpenMs: openedAt === null ? null : startedAt - openedAt,\n });\n }\n };\n socket.onclose = (event) => {\n const closedAt = now();\n emit({\n type: \"socket-close\",\n url,\n code: event?.code,\n reason: event?.reason,\n sinceOpenMs: openedAt === null ? null : closedAt - openedAt,\n });\n const retryAfter = parseRetryAfterFromCloseReason(event?.reason);\n if (retryAfter !== null) {\n emit({\n type: \"retry-after\",\n url,\n delayMs: retryAfter,\n source: \"ws-close\",\n });\n }\n closeHandler?.(event);\n };\n return {\n get protocol() {\n return socket.protocol;\n },\n get readyState() {\n return socket.readyState;\n },\n get onopen() {\n return openHandler;\n },\n set onopen(handler) {\n openHandler = handler;\n },\n get onmessage() {\n return messageHandler;\n },\n set onmessage(handler) {\n messageHandler = handler;\n },\n get onclose() {\n return closeHandler;\n },\n set onclose(handler) {\n closeHandler = handler;\n },\n get onerror() {\n return socket.onerror;\n },\n set onerror(handler) {\n socket.onerror = handler;\n },\n send: (data) => socket.send(data),\n close: (code, reason) => socket.close(code, reason),\n };\n}\n\n/**\n * A `WebSocket`-shaped constructor that dials through `Base` and reports the\n * attach events for `url`. Constructor semantics (`new` returns the facade)\n * match what statewire's `webSocket` option expects.\n */\nexport function instrumentedWebSocketConstructor({\n Base,\n url,\n onEvent,\n now,\n}: {\n Base: AttachWebSocketConstructor;\n url: string;\n onEvent: (event: DeepAgentAttachTimingEvent) => void;\n now?: () => number;\n}): AttachWebSocketConstructor {\n function InstrumentedWebSocket(\n socketUrl: string,\n protocols?: string | string[],\n ) {\n return instrumentAttachSocket({\n socket: new Base(socketUrl, protocols),\n url,\n onEvent,\n ...(now && { now }),\n });\n }\n return InstrumentedWebSocket as unknown as AttachWebSocketConstructor;\n}\n\n/** Structural `fetch` — what statewire's SSE transport calls. */\nexport type AttachFetch = (\n input: string | URL,\n init?: unknown,\n) => Promise<{\n ok: boolean;\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\n/**\n * A `fetch` that reports a `Retry-After` header on refused attach requests\n * (the SSE `POST …/stream`) before handing the response back untouched.\n */\nexport function instrumentedAttachFetch<TFetch extends AttachFetch>({\n baseFetch,\n url,\n onEvent,\n now,\n}: {\n baseFetch: TFetch;\n url: string;\n onEvent: (event: DeepAgentAttachTimingEvent) => void;\n now?: () => number;\n}): TFetch {\n const wrapped: AttachFetch = async (input, init) => {\n const response = await baseFetch(input, init);\n if (!response.ok && (response.status === 503 || response.status === 429)) {\n const delayMs = parseRetryAfterHeaderMs(\n response.headers.get(\"retry-after\"),\n now,\n );\n if (delayMs !== null) {\n try {\n onEvent({ type: \"retry-after\", url, delayMs, source: \"http-header\" });\n } catch {\n // Observers are best-effort.\n }\n }\n }\n return response;\n };\n return wrapped as TFetch;\n}\n\n/** Structural view of the host globals the instrumentation resolves. */\ntype AttachGlobals = {\n WebSocket?: AttachWebSocketConstructor;\n fetch?: AttachFetch;\n};\n\n/**\n * The transport option overrides for one attach: an instrumented `webSocket`\n * (WS) or `fetch` (SSE) that reports attach events for `url` and feeds\n * `Retry-After` hints into the gate. Returns `{}` where the host has no such\n * global (a server render, a non-browser runtime) so the transport keeps its\n * defaults.\n */\nexport function createAttachInstrumentation({\n url,\n sse,\n onEvent,\n retryGate = attachRetryGate,\n now,\n}: {\n url: string;\n sse: boolean;\n onEvent?: ((event: DeepAgentAttachTimingEvent) => void) | undefined;\n retryGate?: AttachRetryGate;\n now?: () => number;\n}): { webSocket?: AttachWebSocketConstructor; fetch?: AttachFetch } {\n const globals = globalThis as AttachGlobals;\n const observe = (event: DeepAgentAttachTimingEvent): void => {\n if (event.type === \"retry-after\")\n retryGate.noteRetryAfter(url, event.delayMs);\n onEvent?.(event);\n };\n if (sse) {\n const baseFetch = globals.fetch;\n if (typeof baseFetch !== \"function\") return {};\n return {\n fetch: instrumentedAttachFetch({\n baseFetch: (input, init) => baseFetch(input, init),\n url,\n onEvent: observe,\n ...(now && { now }),\n }),\n };\n }\n const Base = globals.WebSocket;\n if (typeof Base !== \"function\") return {};\n return {\n webSocket: instrumentedWebSocketConstructor({\n Base,\n url,\n onEvent: observe,\n ...(now && { now }),\n }),\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-state.cjs","sources":["../../../../../agent-runtime/src/statewire-state.ts"],"sourcesContent":["import type { DeepAgentStreamValues } from './types';\n\n/**\n * Typed pre-stream default for the deep-agent statewire state.\n *\n * The statewire client's replicated state is `undefined` (and can transiently\n * be `null`/non-object on a replay boundary) until an attach snapshot applies —\n * `@assistant-ui/react-statewire` derives its `\"loading\"` thread status from\n * exactly that absence, so the raw wire value must keep carrying it. Consumers\n * of OUR conversion layer, however, should never have to null-check the whole\n * state: they read through {@link readDeepAgentStreamValues}, which substitutes\n * this constant while the state is not hydrated.\n *\n * Module-level constant on purpose: selector fallbacks must be referentially\n * stable (`useStatewireState` compares selector results with `Object.is`, so a\n * fresh `[]`/`{}` per call re-renders forever). Frozen so a consumer cannot\n * mutate the shared default.\n */\nconst preStreamState: DeepAgentStreamValues = { messages: [], todos: [] };\nObject.freeze(preStreamState.messages);\nObject.freeze(preStreamState.todos);\nexport const DEEP_AGENT_PRE_STREAM_STATE: DeepAgentStreamValues = Object.freeze(preStreamState);\n\n/**\n * Read the deep-agent stream values off a raw statewire state, substituting\n * {@link DEEP_AGENT_PRE_STREAM_STATE} while the state is not an object yet\n * (pre-attach, or a replay boundary republishing `undefined`).\n *\n * Identity-preserving: a hydrated state returns the same object reference it\n * was given and the pre-stream branch always returns the same module constant,\n * so this is safe to call inside `useStatewireState` selectors.\n */\nexport function readDeepAgentStreamValues(state: unknown): DeepAgentStreamValues {\n return typeof state === 'object' && state !== null\n ? (state as DeepAgentStreamValues)\n : DEEP_AGENT_PRE_STREAM_STATE;\n}\n\n/**\n * Whether a state (raw, or already normalized through\n * {@link readDeepAgentStreamValues}) carries a real wire payload. `false`\n * exactly while the pre-stream default stands in — the explicit signal for\n * code that must distinguish \"empty thread\" from \"no snapshot yet\".\n */\nexport function isDeepAgentStateHydrated(state: unknown): boolean {\n return typeof state === 'object' && state !== null && state !== DEEP_AGENT_PRE_STREAM_STATE;\n}\n"],"names":[],"mappings":";;AAkBA,MAAM,iBAAwC,EAAE,UAAU,IAAI,OAAO,CAAA,EAAC;AACtE,OAAO,OAAO,eAAe,QAAQ;AACrC,OAAO,OAAO,eAAe,KAAK;AAC3B,MAAM,8BAAqD,OAAO,OAAO,cAAc;AAWvF,SAAS,0BAA0B,OAAuC;AAC/E,SAAO,OAAO,UAAU,YAAY,UAAU,OACzC,QACD;AACN;AAQO,SAAS,yBAAyB,OAAyB;AAChE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,UAAU;AAClE;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-state.js","sources":["../../../../../agent-runtime/src/statewire-state.ts"],"sourcesContent":["import type { DeepAgentStreamValues } from './types';\n\n/**\n * Typed pre-stream default for the deep-agent statewire state.\n *\n * The statewire client's replicated state is `undefined` (and can transiently\n * be `null`/non-object on a replay boundary) until an attach snapshot applies —\n * `@assistant-ui/react-statewire` derives its `\"loading\"` thread status from\n * exactly that absence, so the raw wire value must keep carrying it. Consumers\n * of OUR conversion layer, however, should never have to null-check the whole\n * state: they read through {@link readDeepAgentStreamValues}, which substitutes\n * this constant while the state is not hydrated.\n *\n * Module-level constant on purpose: selector fallbacks must be referentially\n * stable (`useStatewireState` compares selector results with `Object.is`, so a\n * fresh `[]`/`{}` per call re-renders forever). Frozen so a consumer cannot\n * mutate the shared default.\n */\nconst preStreamState: DeepAgentStreamValues = { messages: [], todos: [] };\nObject.freeze(preStreamState.messages);\nObject.freeze(preStreamState.todos);\nexport const DEEP_AGENT_PRE_STREAM_STATE: DeepAgentStreamValues = Object.freeze(preStreamState);\n\n/**\n * Read the deep-agent stream values off a raw statewire state, substituting\n * {@link DEEP_AGENT_PRE_STREAM_STATE} while the state is not an object yet\n * (pre-attach, or a replay boundary republishing `undefined`).\n *\n * Identity-preserving: a hydrated state returns the same object reference it\n * was given and the pre-stream branch always returns the same module constant,\n * so this is safe to call inside `useStatewireState` selectors.\n */\nexport function readDeepAgentStreamValues(state: unknown): DeepAgentStreamValues {\n return typeof state === 'object' && state !== null\n ? (state as DeepAgentStreamValues)\n : DEEP_AGENT_PRE_STREAM_STATE;\n}\n\n/**\n * Whether a state (raw, or already normalized through\n * {@link readDeepAgentStreamValues}) carries a real wire payload. `false`\n * exactly while the pre-stream default stands in — the explicit signal for\n * code that must distinguish \"empty thread\" from \"no snapshot yet\".\n */\nexport function isDeepAgentStateHydrated(state: unknown): boolean {\n return typeof state === 'object' && state !== null && state !== DEEP_AGENT_PRE_STREAM_STATE;\n}\n"],"names":[],"mappings":"AAkBA,MAAM,iBAAwC,EAAE,UAAU,IAAI,OAAO,CAAA,EAAC;AACtE,OAAO,OAAO,eAAe,QAAQ;AACrC,OAAO,OAAO,eAAe,KAAK;AAC3B,MAAM,8BAAqD,OAAO,OAAO,cAAc;AAWvF,SAAS,0BAA0B,OAAuC;AAC/E,SAAO,OAAO,UAAU,YAAY,UAAU,OACzC,QACD;AACN;AAQO,SAAS,yBAAyB,OAAyB;AAChE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,UAAU;AAClE;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-suspended.cjs","sources":["../../../../../agent-runtime/src/statewire-suspended.ts"],"sourcesContent":["import { resource } from '@assistant-ui/tap';\nimport type { ResourceElement } from '@assistant-ui/tap';\nimport { useRef, useState } from 'react';\nimport type { Statewire, StatewireClient } from 'statewire';\nimport { StatewireSendError } from 'statewire';\n\nexport interface SuspendedStatewireOptions {\n /** Why the transport is parked; surfaced as the connection message. */\n message: string;\n /**\n * Invoked when a surface asks the parked connection to `reconnect()` (the\n * connection indicator's retry affordance). Hosts that park for a reason\n * only they can clear (e.g. the user has no workspace) omit it.\n */\n onReconnectRequested?: (() => void) | undefined;\n}\n\nconst suspendedCommands = <\n TCommands extends Statewire.Commands,\n>(): Statewire.CommandsProxy<TCommands> =>\n new Proxy({} as Statewire.CommandsProxy<TCommands>, {\n get: (_target, method) => {\n if (typeof method !== 'string') return undefined;\n // Settle like a locally-dead channel: hosts already classify\n // non-\"server\" causes as channel teardown (quiet log, no user error).\n return () =>\n Promise.reject(new StatewireSendError({ fate: 'aborted', cause: 'stream-gone' }));\n },\n });\n\nconst useSuspendedStatewire = <TCommands extends Statewire.Commands = Statewire.Commands>(\n options: SuspendedStatewireOptions\n): Statewire<undefined, TCommands> => {\n const latest = useRef(options);\n latest.current = options;\n // `state` stays undefined so the thread keeps serving its `initialState`\n // (the same contract a live transport has before its attach snapshot).\n const [value] = useState((): Statewire<undefined, TCommands> => {\n const connection: StatewireClient.Connection & { readonly message: string } = {\n status: 'standby',\n reason: 'deferred',\n get message() {\n return latest.current.message;\n },\n reconnect: () => latest.current.onReconnectRequested?.(),\n degraded: false,\n };\n return {\n state: undefined,\n commands: suspendedCommands<TCommands>(),\n connection,\n isBusy: false,\n };\n });\n return value;\n};\n\n/**\n * A statewire transport that never dials.\n *\n * Swapped in for the live WS/SSE transport when attaching is known to be\n * pointless — the host gates it (no workspace) or repeated `unauthorized`\n * denials parked it — so the thread element stays mounted for assistant-ui\n * consumers while the reconnect loop stops entirely.\n */\nexport const StatewireSuspended = resource(useSuspendedStatewire) as <\n TCommands extends Statewire.Commands = Statewire.Commands,\n>(\n options: SuspendedStatewireOptions\n) => ResourceElement<Statewire<undefined, TCommands>>;\n"],"names":["StatewireSendError","useRef","useState","resource"],"mappings":";;;;;AAiBA,MAAM,oBAAoB,MAGxB,IAAI,MAAM,IAA0C;AAAA,EAClD,KAAK,CAAC,SAAS,WAAW;AACxB,QAAI,OAAO,WAAW,SAAU,QAAO;AAGvC,WAAO,MACL,QAAQ,OAAO,IAAIA,UAAAA,mBAAmB,EAAE,MAAM,WAAW,OAAO,cAAA,CAAe,CAAC;AAAA,EACpF;AACF,CAAC;AAEH,MAAM,wBAAwB,CAC5B,YACoC;AACpC,QAAM,SAASC,MAAAA,OAAO,OAAO;AAC7B,SAAO,UAAU;AAGjB,QAAM,CAAC,KAAK,IAAIC,MAAAA,SAAS,MAAuC;AAC9D,UAAM,aAAwE;AAAA,MAC5E,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,IAAI,UAAU;AACZ,eAAO,OAAO,QAAQ;AAAA,MACxB;AAAA,MACA,WAAW,MAAA;;AAAM,kCAAO,SAAQ,yBAAf;AAAA;AAAA,MACjB,UAAU;AAAA,IAAA;AAEZ,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU,kBAAA;AAAA,MACV;AAAA,MACA,QAAQ;AAAA,IAAA;AAAA,EAEZ,CAAC;AACD,SAAO;AACT;AAUO,MAAM,qBAAqBC,IAAAA,SAAS,qBAAqB;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-suspended.js","sources":["../../../../../agent-runtime/src/statewire-suspended.ts"],"sourcesContent":["import { resource } from '@assistant-ui/tap';\nimport type { ResourceElement } from '@assistant-ui/tap';\nimport { useRef, useState } from 'react';\nimport type { Statewire, StatewireClient } from 'statewire';\nimport { StatewireSendError } from 'statewire';\n\nexport interface SuspendedStatewireOptions {\n /** Why the transport is parked; surfaced as the connection message. */\n message: string;\n /**\n * Invoked when a surface asks the parked connection to `reconnect()` (the\n * connection indicator's retry affordance). Hosts that park for a reason\n * only they can clear (e.g. the user has no workspace) omit it.\n */\n onReconnectRequested?: (() => void) | undefined;\n}\n\nconst suspendedCommands = <\n TCommands extends Statewire.Commands,\n>(): Statewire.CommandsProxy<TCommands> =>\n new Proxy({} as Statewire.CommandsProxy<TCommands>, {\n get: (_target, method) => {\n if (typeof method !== 'string') return undefined;\n // Settle like a locally-dead channel: hosts already classify\n // non-\"server\" causes as channel teardown (quiet log, no user error).\n return () =>\n Promise.reject(new StatewireSendError({ fate: 'aborted', cause: 'stream-gone' }));\n },\n });\n\nconst useSuspendedStatewire = <TCommands extends Statewire.Commands = Statewire.Commands>(\n options: SuspendedStatewireOptions\n): Statewire<undefined, TCommands> => {\n const latest = useRef(options);\n latest.current = options;\n // `state` stays undefined so the thread keeps serving its `initialState`\n // (the same contract a live transport has before its attach snapshot).\n const [value] = useState((): Statewire<undefined, TCommands> => {\n const connection: StatewireClient.Connection & { readonly message: string } = {\n status: 'standby',\n reason: 'deferred',\n get message() {\n return latest.current.message;\n },\n reconnect: () => latest.current.onReconnectRequested?.(),\n degraded: false,\n };\n return {\n state: undefined,\n commands: suspendedCommands<TCommands>(),\n connection,\n isBusy: false,\n };\n });\n return value;\n};\n\n/**\n * A statewire transport that never dials.\n *\n * Swapped in for the live WS/SSE transport when attaching is known to be\n * pointless — the host gates it (no workspace) or repeated `unauthorized`\n * denials parked it — so the thread element stays mounted for assistant-ui\n * consumers while the reconnect loop stops entirely.\n */\nexport const StatewireSuspended = resource(useSuspendedStatewire) as <\n TCommands extends Statewire.Commands = Statewire.Commands,\n>(\n options: SuspendedStatewireOptions\n) => ResourceElement<Statewire<undefined, TCommands>>;\n"],"names":[],"mappings":";;;AAiBA,MAAM,oBAAoB,MAGxB,IAAI,MAAM,IAA0C;AAAA,EAClD,KAAK,CAAC,SAAS,WAAW;AACxB,QAAI,OAAO,WAAW,SAAU,QAAO;AAGvC,WAAO,MACL,QAAQ,OAAO,IAAI,mBAAmB,EAAE,MAAM,WAAW,OAAO,cAAA,CAAe,CAAC;AAAA,EACpF;AACF,CAAC;AAEH,MAAM,wBAAwB,CAC5B,YACoC;AACpC,QAAM,SAAS,OAAO,OAAO;AAC7B,SAAO,UAAU;AAGjB,QAAM,CAAC,KAAK,IAAI,SAAS,MAAuC;AAC9D,UAAM,aAAwE;AAAA,MAC5E,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,IAAI,UAAU;AACZ,eAAO,OAAO,QAAQ;AAAA,MACxB;AAAA,MACA,WAAW,MAAA;;AAAM,kCAAO,SAAQ,yBAAf;AAAA;AAAA,MACjB,UAAU;AAAA,IAAA;AAEZ,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU,kBAAA;AAAA,MACV;AAAA,MACA,QAAQ;AAAA,IAAA;AAAA,EAEZ,CAAC;AACD,SAAO;AACT;AAUO,MAAM,qBAAqB,SAAS,qBAAqB;"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const statewire = require("statewire");
|
|
4
|
-
const DEEP_AGENT_STATEWIRE_PROTOCOL = "athena";
|
|
5
|
-
const statewireTransport = (options, { sse = false } = {}) => sse ? statewire.StatewireHttp({ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL, ...options }) : statewire.StatewireWebsocket({
|
|
6
|
-
protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,
|
|
7
|
-
...options
|
|
8
|
-
});
|
|
9
|
-
exports.DEEP_AGENT_STATEWIRE_PROTOCOL = DEEP_AGENT_STATEWIRE_PROTOCOL;
|
|
10
|
-
exports.statewireTransport = statewireTransport;
|
|
11
|
-
//# sourceMappingURL=statewire-transport.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-transport.cjs","sources":["../../../../../agent-runtime/src/statewire-transport.ts"],"sourcesContent":["import { type Statewire, type StatewireClient, StatewireHttp, StatewireWebsocket } from 'statewire';\nimport type { DeepAgentStreamValues } from './types';\n\nexport type StatewireTransportOptions = StatewireClient.HttpOptions &\n StatewireClient.WebsocketOptions;\n\n/**\n * The application protocol the deep-agent transport stamps on every command.\n * The thread client must declare the same protocol: statewire's core applies\n * its optimistic folds only to commands of its own protocol, so a mismatch\n * leaves a send invisible until the server's frame lands.\n */\nexport const DEEP_AGENT_STATEWIRE_PROTOCOL = 'athena';\n\n/**\n * Build the deep-agent statewire transport: WebSocket by default, SSE when the\n * host app's toggle asks for it. The toggle source is host-specific (env var,\n * extension flag, config store), so the caller resolves it to a boolean.\n *\n * `TState` defaults to the deep-agent wire shape: the thread mounts that make\n * up nearly every caller get a typed state without naming it, and the other\n * mounts (the conversation index) name theirs.\n */\nexport const statewireTransport = <\n TState = DeepAgentStreamValues,\n TCommands extends Statewire.Commands = Statewire.Commands,\n>(\n options: StatewireTransportOptions,\n { sse = false }: { sse?: boolean } = {}\n) =>\n sse\n ? StatewireHttp<TState, TCommands>({ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL, ...options })\n : StatewireWebsocket<TState, TCommands>({\n protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,\n ...options,\n });\n"],"names":["StatewireHttp","StatewireWebsocket"],"mappings":";;;AAYO,MAAM,gCAAgC;AAWtC,MAAM,qBAAqB,CAIhC,SACA,EAAE,MAAM,MAAA,IAA6B,CAAA,MAErC,MACIA,UAAAA,cAAiC,EAAE,UAAU,+BAA+B,GAAG,QAAA,CAAS,IACxFC,UAAAA,mBAAsC;AAAA,EACpC,UAAU;AAAA,EACV,GAAG;AACL,CAAC;;;"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { StatewireHttp, StatewireWebsocket } from "statewire";
|
|
2
|
-
const DEEP_AGENT_STATEWIRE_PROTOCOL = "athena";
|
|
3
|
-
const statewireTransport = (options, { sse = false } = {}) => sse ? StatewireHttp({ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL, ...options }) : StatewireWebsocket({
|
|
4
|
-
protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,
|
|
5
|
-
...options
|
|
6
|
-
});
|
|
7
|
-
export {
|
|
8
|
-
DEEP_AGENT_STATEWIRE_PROTOCOL,
|
|
9
|
-
statewireTransport
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=statewire-transport.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statewire-transport.js","sources":["../../../../../agent-runtime/src/statewire-transport.ts"],"sourcesContent":["import { type Statewire, type StatewireClient, StatewireHttp, StatewireWebsocket } from 'statewire';\nimport type { DeepAgentStreamValues } from './types';\n\nexport type StatewireTransportOptions = StatewireClient.HttpOptions &\n StatewireClient.WebsocketOptions;\n\n/**\n * The application protocol the deep-agent transport stamps on every command.\n * The thread client must declare the same protocol: statewire's core applies\n * its optimistic folds only to commands of its own protocol, so a mismatch\n * leaves a send invisible until the server's frame lands.\n */\nexport const DEEP_AGENT_STATEWIRE_PROTOCOL = 'athena';\n\n/**\n * Build the deep-agent statewire transport: WebSocket by default, SSE when the\n * host app's toggle asks for it. The toggle source is host-specific (env var,\n * extension flag, config store), so the caller resolves it to a boolean.\n *\n * `TState` defaults to the deep-agent wire shape: the thread mounts that make\n * up nearly every caller get a typed state without naming it, and the other\n * mounts (the conversation index) name theirs.\n */\nexport const statewireTransport = <\n TState = DeepAgentStreamValues,\n TCommands extends Statewire.Commands = Statewire.Commands,\n>(\n options: StatewireTransportOptions,\n { sse = false }: { sse?: boolean } = {}\n) =>\n sse\n ? StatewireHttp<TState, TCommands>({ protocol: DEEP_AGENT_STATEWIRE_PROTOCOL, ...options })\n : StatewireWebsocket<TState, TCommands>({\n protocol: DEEP_AGENT_STATEWIRE_PROTOCOL,\n ...options,\n });\n"],"names":[],"mappings":";AAYO,MAAM,gCAAgC;AAWtC,MAAM,qBAAqB,CAIhC,SACA,EAAE,MAAM,MAAA,IAA6B,CAAA,MAErC,MACI,cAAiC,EAAE,UAAU,+BAA+B,GAAG,QAAA,CAAS,IACxF,mBAAsC;AAAA,EACpC,UAAU;AAAA,EACV,GAAG;AACL,CAAC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"streaming-markdown.cjs","sources":["../../../../../agent-runtime/src/streaming-markdown.ts"],"sourcesContent":["/**\n * Hide a markdown link the stream has not finished writing.\n *\n * The agent mentions and cites assets as markdown links —\n * `[@Considerations/Notes](https://…/dashboard/spaces/?asset_ids=asset_…&type=document&anchor=pm_node&node_id=…&excerpt=…)`\n * — which every chat surface renders as a mention chip once the link is\n * complete. While it is still arriving, a markdown parser sees a bracketed\n * label followed by a bare URL: the label shows as raw text and the half URL\n * linkifies into a live link whose target changes with every batch, for as\n * long as the destination takes to stream (an `excerpt` parameter can run to\n * hundreds of characters). Then the closing `)` lands and the run is rebuilt\n * as a chip.\n *\n * While a part is streaming, cut the text at the start of a trailing link\n * whose `)` has not arrived; it reappears whole, as a chip, in the batch that\n * closes it. Every link and image destination is held — a web citation\n * flickers the same way — and so is every bracket still undecided on the\n * line: an unfinished `[label`, and a closed `[label]` at the very end of the\n * text, which waits one batch for its `(`. Holding the label too is what\n * makes the visible text only ever grow — a link never shows as a label and\n * then retracts when its destination starts — and that append-only property\n * is what keeps Olympus's incremental streaming apply on its cheap path.\n * Brackets match innermost-first, as CommonMark does, and the hold starts at\n * the outermost undecided bracket the size bound allows, so an unmatched\n * bracket in prose hides at most a phrase, briefly, and never masks a later\n * mention. A construct that crosses a line break or runs past its bound is\n * released so a malformed link cannot hide the rest of the message; fenced\n * code (including fences inside blockquotes), indented code and inline code\n * are left untouched; and settled text is never passed through this\n * function, so a link the model genuinely left open shows raw once the part\n * completes.\n *\n * Shared by every chat surface (Olympus, the React SDK hosts, mobile) so the\n * hold behaves identically wherever a transcript streams. Pure string work,\n * linear in the text.\n */\n\n/** An open bracket further back than this is prose, not a label being streamed. */\nexport const UNTERMINATED_LABEL_HOLD_LIMIT = 200;\n/** A link (label and destination) longer than this is broken, not streaming. */\nexport const UNTERMINATED_LINK_HOLD_LIMIT = 1500;\n\n/** Opening or closing code fence: three or more backticks or tildes, then the info string. */\nconst FENCE_LINE = /^\\s*(`{3,}|~{3,})(.*)$/;\n/** Leading blockquote markers (`> `, `>>`, each indented at most three spaces). */\nconst BLOCKQUOTE_PREFIX = /^(?: {0,3}>[ \\t]?)+/;\n/** Bullet or ordered list marker at the start of a line, with the spaces that follow it. */\nconst LIST_MARKER = /^([ \\t]*)([-*+]|\\d{1,9}[.)])([ \\t]+)/;\n\n/**\n * The streamed text with a trailing, still-undecided markdown link or image\n * removed. Text without such a construct is returned as is, and the result is\n * always a prefix of `text`.\n */\nexport function hideUnterminatedMarkdownLink(text: string): string {\n if (!text.includes(\"[\")) return text;\n\n const start = scanTrailingLink(text);\n return start === null ? text : text.slice(0, start);\n}\n\n/** Length of the run of `char` starting at `index`. */\nfunction runLength(text: string, index: number, char: string): number {\n let end = index;\n while (end < text.length && text[end] === char) end += 1;\n return end - index;\n}\n\n/**\n * Index just past the backtick run of exactly `length` that closes a code\n * span opened before `from`, or -1 when none arrives before the paragraph\n * ends (a blank line) — an open span runs to the end of the text.\n */\nfunction codeSpanEnd(text: string, from: number, length: number): number {\n const blankLine = text.indexOf(\"\\n\\n\", from);\n const paragraphEnd = blankLine === -1 ? text.length : blankLine;\n let cursor = from;\n while (cursor < paragraphEnd) {\n const next = text.indexOf(\"`\", cursor);\n if (next === -1 || next >= paragraphEnd) return -1;\n const run = runLength(text, next, \"`\");\n if (run === length) return next + run;\n cursor = next + run;\n }\n return -1;\n}\n\n/**\n * The fence run a line opens or closes, or null. A backtick fence's info\n * string cannot contain a backtick (that is inline code, not a fence).\n */\nfunction fenceRun(line: string): string | null {\n const match = FENCE_LINE.exec(line);\n if (!match) return null;\n if (match[1][0] === \"`\" && match[2].includes(\"`\")) return null;\n return match[1];\n}\n\n/** Columns of leading whitespace on a line; a tab counts as four. */\nfunction leadingIndent(line: string): number {\n let indent = 0;\n for (const char of line) {\n if (char === \" \") indent += 1;\n else if (char === \"\\t\") indent += 4;\n else break;\n }\n return indent;\n}\n\n/**\n * Tracks the block structure the inline scan must not read: fenced code\n * (also inside blockquotes), indented code, and the list nesting that decides\n * how deep an indent has to be before it is code rather than a nested item\n * or a continuation paragraph. Fed one line at a time, in order.\n */\nfunction createBlockTracker() {\n let fence: string | null = null;\n let indentedCode = false;\n let previousBlank = true;\n /** Content indents of the open list items, outermost first. */\n const listContentIndents: number[] = [];\n\n /** Whether `line` is prose the inline scan should read. */\n return (line: string): boolean => {\n const content = line.replace(BLOCKQUOTE_PREFIX, \"\");\n const blank = content.trim() === \"\";\n try {\n const run = fenceRun(content);\n if (fence !== null) {\n // Code: a run of the same character at least as long as the opener\n // closes the block; everything else on the line is content.\n if (run !== null && run[0] === fence[0] && run.length >= fence.length) fence = null;\n return false;\n }\n if (run !== null) {\n fence = run;\n indentedCode = false;\n return false;\n }\n\n // Indented code needs four columns beyond the innermost open list\n // item's content — less than that is a nested item or a continuation\n // paragraph — and cannot interrupt a paragraph.\n const indent = leadingIndent(line);\n const codeIndent = (listContentIndents.at(-1) ?? 0) + 4;\n if (indentedCode) {\n if (blank || indent >= codeIndent) return false;\n indentedCode = false;\n } else if (!blank && previousBlank && indent >= codeIndent) {\n indentedCode = true;\n return false;\n }\n if (blank) return true;\n\n const marker = LIST_MARKER.exec(line);\n if (marker) {\n const markerIndent = leadingIndent(marker[1]);\n while ((listContentIndents.at(-1) ?? -1) > markerIndent) listContentIndents.pop();\n // Five or more spaces after the marker start an indented code block\n // inside the item, so the content column is one past the marker.\n const gap = marker[3].length;\n const width = marker[2].length;\n listContentIndents.push(markerIndent + width + (gap > 4 ? 1 : gap));\n } else {\n while (listContentIndents.length > 0 && indent < (listContentIndents.at(-1) ?? 0)) {\n listContentIndents.pop();\n }\n }\n return true;\n } finally {\n previousBlank = blank;\n }\n };\n}\n\n/**\n * The CommonMark link-destination grammar after `](`: optional whitespace,\n * then either `<…>` or a bare destination whose parentheses must balance,\n * then optional whitespace and a `\"…\"`, `'…'` or `(…)` title, then `)`.\n */\ninterface Destination {\n start: number;\n phase: \"start\" | \"angle\" | \"bare\" | \"after-destination\" | \"title\" | \"after-title\";\n depth: number;\n titleClose: string;\n}\n\nfunction advanceDestination(\n destination: Destination,\n char: string,\n): \"open\" | \"closed\" | \"invalid\" {\n const space = char === \" \" || char === \"\\t\";\n switch (destination.phase) {\n case \"start\":\n if (space) return \"open\";\n if (char === \")\") return \"closed\";\n if (char === \"<\") {\n destination.phase = \"angle\";\n return \"open\";\n }\n destination.phase = \"bare\";\n return advanceDestination(destination, char);\n case \"angle\":\n if (char === \">\") destination.phase = \"after-destination\";\n return \"open\";\n case \"bare\":\n if (char === \"(\") {\n destination.depth += 1;\n return \"open\";\n }\n if (char === \")\") {\n if (destination.depth === 0) return \"closed\";\n destination.depth -= 1;\n return \"open\";\n }\n if (space) destination.phase = \"after-destination\";\n return \"open\";\n case \"after-destination\":\n if (space) return \"open\";\n if (char === \")\") return \"closed\";\n if (char === '\"' || char === \"'\" || char === \"(\") {\n destination.phase = \"title\";\n destination.titleClose = char === \"(\" ? \")\" : char;\n return \"open\";\n }\n return \"invalid\";\n case \"title\":\n if (char === destination.titleClose) destination.phase = \"after-title\";\n return \"open\";\n case \"after-title\":\n if (space) return \"open\";\n return char === \")\" ? \"closed\" : \"invalid\";\n }\n}\n\n/**\n * Where the text should be cut for the link construct still undecided at its\n * very end, or null when nothing is open: the outermost unclosed `[` within\n * the label bound, else the `[` of a closed label or open destination that\n * is still within its own bound.\n */\nfunction scanTrailingLink(text: string): number | null {\n /** Positions of the `[` not yet closed on this line, outermost first. */\n let labelStarts: number[] = [];\n /** The `[` whose `]` is the last character of the text — `(` may be next. */\n let closedAtEnd: number | null = null;\n let destination: Destination | null = null;\n const isProseLine = createBlockTracker();\n let index = 0;\n let lineStart = true;\n\n const resetInline = (): void => {\n labelStarts = [];\n closedAtEnd = null;\n destination = null;\n };\n const result = (): number | null => {\n // Every bracket still open on the line may yet become a link, so hold\n // from the outermost one the bound allows: the visible text then only\n // ever grows, even as an inner link opens and closes.\n for (const start of labelStarts) {\n if (text.length - start <= UNTERMINATED_LABEL_HOLD_LIMIT) return start;\n }\n if (destination !== null) {\n return text.length - destination.start <= UNTERMINATED_LINK_HOLD_LIMIT\n ? destination.start\n : null;\n }\n if (closedAtEnd !== null && text.length - closedAtEnd <= UNTERMINATED_LABEL_HOLD_LIMIT) {\n return closedAtEnd;\n }\n return null;\n };\n\n while (index < text.length) {\n if (lineStart) {\n lineStart = false;\n let lineEnd = text.indexOf(\"\\n\", index);\n if (lineEnd === -1) lineEnd = text.length;\n if (!isProseLine(text.slice(index, lineEnd))) {\n // A block boundary or a code line: no inline construct survives it.\n resetInline();\n index = lineEnd + 1;\n lineStart = true;\n continue;\n }\n }\n\n const char = text[index];\n if (char === \"\\n\") {\n // Labels and destinations do not span lines in a streamed message.\n resetInline();\n lineStart = true;\n index += 1;\n continue;\n }\n if (char === \"\\\\\") {\n index += 2;\n continue;\n }\n if (char === \"`\") {\n const length = runLength(text, index, \"`\");\n const end = codeSpanEnd(text, index + length, length);\n // An open code span runs to the end of the text: nothing after it is\n // markdown, and a construct opened before it is still being written.\n if (end === -1) return result();\n index = end;\n continue;\n }\n\n if (destination !== null) {\n const verdict = advanceDestination(destination, char);\n if (verdict !== \"open\") destination = null;\n } else if (char === \"[\") {\n // An image's `!` stays visible: cutting before it would take back a\n // character already shown, and the renderer consumes it at completion.\n labelStarts.push(index);\n } else if (char === \"]\") {\n // Innermost-first, as CommonMark matches brackets: the `]` closes the\n // most recent `[`, and only that pair can become a link.\n const start = labelStarts.pop();\n if (start !== undefined) {\n if (index + 1 >= text.length) {\n closedAtEnd = start;\n } else if (text[index + 1] === \"(\") {\n destination = { start, phase: \"start\", depth: 0, titleClose: \"\" };\n index += 1;\n }\n }\n }\n index += 1;\n }\n\n return result();\n}\n"],"names":[],"mappings":"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"}
|