@tai42/studio-sdk 16.0.0 → 17.0.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/dist/components/code-block.js +1 -1
- package/dist/components/code-block.js.map +1 -1
- package/dist/components/confirm-dialog.js +1 -1
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-field.js +1 -1
- package/dist/components/copy-field.js.map +1 -1
- package/dist/components/date-range-picker.d.ts.map +1 -1
- package/dist/components/date-range-picker.js.map +1 -1
- package/dist/components/dialog.d.ts.map +1 -1
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/dirty-guard.d.ts.map +1 -1
- package/dist/components/dirty-guard.js +1 -1
- package/dist/components/dirty-guard.js.map +1 -1
- package/dist/components/explorer-body.d.ts +49 -0
- package/dist/components/explorer-body.d.ts.map +1 -0
- package/dist/components/explorer-body.js +67 -0
- package/dist/components/explorer-body.js.map +1 -0
- package/dist/components/explorer-page-size.d.ts +4 -0
- package/dist/components/explorer-page-size.d.ts.map +1 -0
- package/dist/components/explorer-page-size.js +36 -0
- package/dist/components/explorer-page-size.js.map +1 -0
- package/dist/components/explorer-tag-filter.d.ts +17 -0
- package/dist/components/explorer-tag-filter.d.ts.map +1 -0
- package/dist/components/explorer-tag-filter.js +29 -0
- package/dist/components/explorer-tag-filter.js.map +1 -0
- package/dist/components/explorer-tags.d.ts +22 -0
- package/dist/components/explorer-tags.d.ts.map +1 -0
- package/dist/components/explorer-tags.js +51 -0
- package/dist/components/explorer-tags.js.map +1 -0
- package/dist/components/explorer-view.d.ts +3 -17
- package/dist/components/explorer-view.d.ts.map +1 -1
- package/dist/components/explorer-view.js +53 -170
- package/dist/components/explorer-view.js.map +1 -1
- package/dist/components/extension-combo-builder.d.ts +1 -6
- package/dist/components/extension-combo-builder.d.ts.map +1 -1
- package/dist/components/extension-combo-builder.js +4 -79
- package/dist/components/extension-combo-builder.js.map +1 -1
- package/dist/components/extension-combo-parts.d.ts +44 -0
- package/dist/components/extension-combo-parts.d.ts.map +1 -0
- package/dist/components/extension-combo-parts.js +52 -0
- package/dist/components/extension-combo-parts.js.map +1 -0
- package/dist/components/field.d.ts +8 -0
- package/dist/components/field.d.ts.map +1 -1
- package/dist/components/field.js +0 -8
- package/dist/components/field.js.map +1 -1
- package/dist/components/fleet-report.d.ts +1 -1
- package/dist/components/fleet-report.d.ts.map +1 -1
- package/dist/components/fleet-report.js +51 -37
- package/dist/components/fleet-report.js.map +1 -1
- package/dist/components/folder-nav.d.ts.map +1 -1
- package/dist/components/folder-nav.js +1 -1
- package/dist/components/folder-nav.js.map +1 -1
- package/dist/components/form-dialog.d.ts.map +1 -1
- package/dist/components/form-dialog.js +1 -1
- package/dist/components/form-dialog.js.map +1 -1
- package/dist/components/icons/chrome-marks.d.ts +50 -0
- package/dist/components/icons/chrome-marks.d.ts.map +1 -0
- package/dist/components/icons/chrome-marks.js +51 -0
- package/dist/components/icons/chrome-marks.js.map +1 -0
- package/dist/components/icons/icon-frame.d.ts +29 -0
- package/dist/components/icons/icon-frame.d.ts.map +1 -0
- package/dist/components/icons/icon-frame.js +19 -0
- package/dist/components/icons/icon-frame.js.map +1 -0
- package/dist/components/icons/index.d.ts +40 -0
- package/dist/components/icons/index.d.ts.map +1 -0
- package/dist/components/icons/index.js +39 -0
- package/dist/components/icons/index.js.map +1 -0
- package/dist/components/icons/nav-map.d.ts +14 -0
- package/dist/components/icons/nav-map.d.ts.map +1 -0
- package/dist/components/icons/nav-map.js +28 -0
- package/dist/components/icons/nav-map.js.map +1 -0
- package/dist/components/icons/nav-marks.d.ts +44 -0
- package/dist/components/icons/nav-marks.d.ts.map +1 -0
- package/dist/components/icons/nav-marks.js +45 -0
- package/dist/components/icons/nav-marks.js.map +1 -0
- package/dist/components/icons/status-marks.d.ts +21 -0
- package/dist/components/icons/status-marks.d.ts.map +1 -0
- package/dist/components/icons/status-marks.js +22 -0
- package/dist/components/icons/status-marks.js.map +1 -0
- package/dist/components/icons/theme-marks.d.ts +9 -0
- package/dist/components/icons/theme-marks.d.ts.map +1 -0
- package/dist/components/icons/theme-marks.js +10 -0
- package/dist/components/icons/theme-marks.js.map +1 -0
- package/dist/components/inputs.js.map +1 -1
- package/dist/components/json-diff.d.ts.map +1 -1
- package/dist/components/json-diff.js +1 -3
- package/dist/components/json-diff.js.map +1 -1
- package/dist/components/json-tree-context.d.ts +13 -0
- package/dist/components/json-tree-context.d.ts.map +1 -0
- package/dist/components/json-tree-context.js +15 -0
- package/dist/components/json-tree-context.js.map +1 -0
- package/dist/components/json-tree-model.d.ts +56 -0
- package/dist/components/json-tree-model.d.ts.map +1 -0
- package/dist/components/json-tree-model.js +125 -0
- package/dist/components/json-tree-model.js.map +1 -0
- package/dist/components/json-tree-nodes.d.ts +35 -0
- package/dist/components/json-tree-nodes.d.ts.map +1 -0
- package/dist/components/json-tree-nodes.js +113 -0
- package/dist/components/json-tree-nodes.js.map +1 -0
- package/dist/components/json-tree.d.ts +20 -0
- package/dist/components/json-tree.d.ts.map +1 -1
- package/dist/components/json-tree.js +7 -315
- package/dist/components/json-tree.js.map +1 -1
- package/dist/components/json-value-format.d.ts +20 -0
- package/dist/components/json-value-format.d.ts.map +1 -0
- package/dist/components/json-value-format.js +63 -0
- package/dist/components/json-value-format.js.map +1 -0
- package/dist/components/markdown-blocks.d.ts +38 -0
- package/dist/components/markdown-blocks.d.ts.map +1 -0
- package/dist/components/markdown-blocks.js +168 -0
- package/dist/components/markdown-blocks.js.map +1 -0
- package/dist/components/markdown-inline.d.ts +24 -0
- package/dist/components/markdown-inline.d.ts.map +1 -0
- package/dist/components/markdown-inline.js +257 -0
- package/dist/components/markdown-inline.js.map +1 -0
- package/dist/components/markdown.d.ts +3 -52
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +4 -376
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/observer-registry.d.ts +38 -0
- package/dist/components/observer-registry.d.ts.map +1 -0
- package/dist/components/observer-registry.js +131 -0
- package/dist/components/observer-registry.js.map +1 -0
- package/dist/components/overflow-measure.d.ts +38 -0
- package/dist/components/overflow-measure.d.ts.map +1 -0
- package/dist/components/overflow-measure.js +54 -0
- package/dist/components/overflow-measure.js.map +1 -0
- package/dist/components/page-fill.d.ts.map +1 -1
- package/dist/components/page-fill.js.map +1 -1
- package/dist/components/prose-regions.d.ts +81 -0
- package/dist/components/prose-regions.d.ts.map +1 -0
- package/dist/components/prose-regions.js +128 -0
- package/dist/components/prose-regions.js.map +1 -0
- package/dist/components/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/scroll-region.d.ts +24 -0
- package/dist/components/scroll-region.d.ts.map +1 -1
- package/dist/components/scroll-region.js +3 -322
- package/dist/components/scroll-region.js.map +1 -1
- package/dist/components/tab-stop-parse.d.ts +53 -0
- package/dist/components/tab-stop-parse.d.ts.map +1 -0
- package/dist/components/tab-stop-parse.js +202 -0
- package/dist/components/tab-stop-parse.js.map +1 -0
- package/dist/components/table.d.ts +1 -1
- package/dist/components/table.d.ts.map +1 -1
- package/dist/components/tags.d.ts.map +1 -1
- package/dist/components/tags.js +1 -1
- package/dist/components/tags.js.map +1 -1
- package/dist/components/templated-text-field.d.ts +1 -1
- package/dist/components/templated-text-field.d.ts.map +1 -1
- package/dist/components/templated-text-field.js +10 -174
- package/dist/components/templated-text-field.js.map +1 -1
- package/dist/components/templated-text-kwargs-editor.d.ts +24 -0
- package/dist/components/templated-text-kwargs-editor.d.ts.map +1 -0
- package/dist/components/templated-text-kwargs-editor.js +48 -0
- package/dist/components/templated-text-kwargs-editor.js.map +1 -0
- package/dist/components/templated-text-kwargs.d.ts +37 -0
- package/dist/components/templated-text-kwargs.d.ts.map +1 -0
- package/dist/components/templated-text-kwargs.js +66 -0
- package/dist/components/templated-text-kwargs.js.map +1 -0
- package/dist/components/templated-text-source.d.ts +33 -0
- package/dist/components/templated-text-source.d.ts.map +1 -0
- package/dist/components/templated-text-source.js +53 -0
- package/dist/components/templated-text-source.js.map +1 -0
- package/dist/components/tool-picker-parts.d.ts +39 -0
- package/dist/components/tool-picker-parts.d.ts.map +1 -0
- package/dist/components/tool-picker-parts.js +87 -0
- package/dist/components/tool-picker-parts.js.map +1 -0
- package/dist/components/tool-picker.d.ts +1 -2
- package/dist/components/tool-picker.d.ts.map +1 -1
- package/dist/components/tool-picker.js +4 -66
- package/dist/components/tool-picker.js.map +1 -1
- package/dist/components/version-history-panel.d.ts.map +1 -1
- package/dist/components/version-history-panel.js +7 -34
- package/dist/components/version-history-panel.js.map +1 -1
- package/dist/components/version-history-rows.d.ts +48 -0
- package/dist/components/version-history-rows.d.ts.map +1 -0
- package/dist/components/version-history-rows.js +51 -0
- package/dist/components/version-history-rows.js.map +1 -0
- package/dist/components/view-toggle.d.ts.map +1 -1
- package/dist/components/view-toggle.js.map +1 -1
- package/dist/element-slot.d.ts +11 -0
- package/dist/element-slot.d.ts.map +1 -1
- package/dist/element-slot.js +0 -11
- package/dist/element-slot.js.map +1 -1
- package/dist/guards.d.ts +6 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/guards.js +8 -0
- package/dist/guards.js.map +1 -0
- package/dist/hooks/interactions-connection.d.ts +43 -0
- package/dist/hooks/interactions-connection.d.ts.map +1 -0
- package/dist/hooks/interactions-connection.js +171 -0
- package/dist/hooks/interactions-connection.js.map +1 -0
- package/dist/hooks/interactions-stream.d.ts +80 -0
- package/dist/hooks/interactions-stream.d.ts.map +1 -0
- package/dist/hooks/interactions-stream.js +233 -0
- package/dist/hooks/interactions-stream.js.map +1 -0
- package/dist/hooks/useApi.d.ts +2259 -2189
- package/dist/hooks/useApi.d.ts.map +1 -1
- package/dist/hooks/useApi.js +0 -11
- package/dist/hooks/useApi.js.map +1 -1
- package/dist/hooks/useAuth.d.ts +6 -0
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +0 -6
- package/dist/hooks/useAuth.js.map +1 -1
- package/dist/hooks/useCapabilities.d.ts +18 -2
- package/dist/hooks/useCapabilities.d.ts.map +1 -1
- package/dist/hooks/useCapabilities.js +1 -17
- package/dist/hooks/useCapabilities.js.map +1 -1
- package/dist/hooks/useSse.d.ts +34 -6
- package/dist/hooks/useSse.d.ts.map +1 -1
- package/dist/hooks/useSse.js +27 -471
- package/dist/hooks/useSse.js.map +1 -1
- package/dist/hooks/useSystemKinds.d.ts +1 -1
- package/dist/hooks/useSystemKinds.d.ts.map +1 -1
- package/dist/hooks/useSystemKinds.js +0 -18
- package/dist/hooks/useSystemKinds.js.map +1 -1
- package/dist/hooks/useTheme.d.ts +15 -0
- package/dist/hooks/useTheme.d.ts.map +1 -1
- package/dist/hooks/useTheme.js +0 -15
- package/dist/hooks/useTheme.js.map +1 -1
- package/dist/hooks/useToolDisplayNames.d.ts +0 -14
- package/dist/hooks/useToolDisplayNames.d.ts.map +1 -1
- package/dist/hooks/useToolDisplayNames.js +0 -14
- package/dist/hooks/useToolDisplayNames.js.map +1 -1
- package/dist/host.d.ts +3 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +2 -9
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +103 -103
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -62
- package/dist/index.js.map +1 -1
- package/dist/navigation/app-link.d.ts +25 -0
- package/dist/navigation/app-link.d.ts.map +1 -0
- package/dist/navigation/app-link.js +33 -0
- package/dist/navigation/app-link.js.map +1 -0
- package/dist/navigation/context.d.ts +3 -20
- package/dist/navigation/context.d.ts.map +1 -1
- package/dist/navigation/context.js +7 -267
- package/dist/navigation/context.js.map +1 -1
- package/dist/navigation/entry-state.d.ts +23 -0
- package/dist/navigation/entry-state.d.ts.map +1 -0
- package/dist/navigation/entry-state.js +39 -0
- package/dist/navigation/entry-state.js.map +1 -0
- package/dist/navigation/guard-registry.d.ts +30 -0
- package/dist/navigation/guard-registry.d.ts.map +1 -0
- package/dist/navigation/guard-registry.js +46 -0
- package/dist/navigation/guard-registry.js.map +1 -0
- package/dist/navigation/guarded-navigation.d.ts +24 -0
- package/dist/navigation/guarded-navigation.d.ts.map +1 -0
- package/dist/navigation/guarded-navigation.js +102 -0
- package/dist/navigation/guarded-navigation.js.map +1 -0
- package/dist/navigation/index.d.ts +5 -4
- package/dist/navigation/index.d.ts.map +1 -1
- package/dist/navigation/index.js +2 -1
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/types.d.ts +8 -11
- package/dist/navigation/types.d.ts.map +1 -1
- package/dist/navigation/use-back-forward-guard.d.ts +12 -0
- package/dist/navigation/use-back-forward-guard.d.ts.map +1 -0
- package/dist/navigation/use-back-forward-guard.js +75 -0
- package/dist/navigation/use-back-forward-guard.js.map +1 -0
- package/dist/navigation/use-search-commit.d.ts.map +1 -1
- package/dist/navigation/use-search-commit.js.map +1 -1
- package/dist/plugin/host-state.d.ts.map +1 -1
- package/dist/plugin/host-state.js.map +1 -1
- package/dist/plugin/registry.d.ts +6 -15
- package/dist/plugin/registry.d.ts.map +1 -1
- package/dist/plugin/registry.js +83 -75
- package/dist/plugin/registry.js.map +1 -1
- package/dist/schema-editor/SchemaEditor.d.ts.map +1 -1
- package/dist/schema-editor/SchemaEditor.js.map +1 -1
- package/dist/schema-editor/index.d.ts +3 -3
- package/dist/schema-editor/index.d.ts.map +1 -1
- package/dist/schema-editor/index.js +1 -6
- package/dist/schema-editor/index.js.map +1 -1
- package/dist/schema-form/SecretRefField.d.ts +18 -3
- package/dist/schema-form/SecretRefField.d.ts.map +1 -1
- package/dist/schema-form/SecretRefField.js +21 -26
- package/dist/schema-form/SecretRefField.js.map +1 -1
- package/dist/schema-form/classify.d.ts +1 -113
- package/dist/schema-form/classify.d.ts.map +1 -1
- package/dist/schema-form/classify.js +174 -246
- package/dist/schema-form/classify.js.map +1 -1
- package/dist/schema-form/context.d.ts +6 -0
- package/dist/schema-form/context.d.ts.map +1 -1
- package/dist/schema-form/context.js +0 -6
- package/dist/schema-form/context.js.map +1 -1
- package/dist/schema-form/default-value.d.ts.map +1 -1
- package/dist/schema-form/default-value.js +42 -19
- package/dist/schema-form/default-value.js.map +1 -1
- package/dist/schema-form/enum-field.d.ts +1 -1
- package/dist/schema-form/enum-field.d.ts.map +1 -1
- package/dist/schema-form/field-group.d.ts +6 -0
- package/dist/schema-form/field-group.d.ts.map +1 -1
- package/dist/schema-form/field-group.js +0 -6
- package/dist/schema-form/field-group.js.map +1 -1
- package/dist/schema-form/field-model.d.ts +121 -0
- package/dist/schema-form/field-model.d.ts.map +1 -0
- package/dist/schema-form/field-model.js +2 -0
- package/dist/schema-form/field-model.js.map +1 -0
- package/dist/schema-form/field-node.d.ts.map +1 -1
- package/dist/schema-form/field-node.js +25 -14
- package/dist/schema-form/field-node.js.map +1 -1
- package/dist/schema-form/index.d.ts +8 -8
- package/dist/schema-form/index.d.ts.map +1 -1
- package/dist/schema-form/index.js +3 -34
- package/dist/schema-form/index.js.map +1 -1
- package/dist/schema-form/json-field.d.ts +19 -0
- package/dist/schema-form/json-field.d.ts.map +1 -1
- package/dist/schema-form/json-field.js +0 -19
- package/dist/schema-form/json-field.js.map +1 -1
- package/dist/schema-form/media-field.d.ts +1 -1
- package/dist/schema-form/media-field.d.ts.map +1 -1
- package/dist/schema-form/media-field.js +49 -30
- package/dist/schema-form/media-field.js.map +1 -1
- package/dist/schema-form/record-field.d.ts +12 -0
- package/dist/schema-form/record-field.d.ts.map +1 -1
- package/dist/schema-form/record-field.js +1 -13
- package/dist/schema-form/record-field.js.map +1 -1
- package/dist/schema-form/string-annotations.d.ts +20 -0
- package/dist/schema-form/string-annotations.d.ts.map +1 -0
- package/dist/schema-form/string-annotations.js +105 -0
- package/dist/schema-form/string-annotations.js.map +1 -0
- package/dist/schema-form/string-field.d.ts +1 -1
- package/dist/schema-form/string-field.d.ts.map +1 -1
- package/dist/schema-form/string-field.js +1 -1
- package/dist/schema-form/string-field.js.map +1 -1
- package/dist/schema-form/union-field.d.ts +1 -1
- package/dist/schema-form/union-field.d.ts.map +1 -1
- package/dist/schema-form/union-field.js.map +1 -1
- package/dist/schema-form/union.d.ts +8 -1
- package/dist/schema-form/union.d.ts.map +1 -1
- package/dist/schema-form/union.js +0 -7
- package/dist/schema-form/union.js.map +1 -1
- package/dist/schema-form/validate.d.ts.map +1 -1
- package/dist/schema-form/validate.js +126 -105
- package/dist/schema-form/validate.js.map +1 -1
- package/dist/state-binding/AdapterMapping.d.ts.map +1 -1
- package/dist/state-binding/AdapterMapping.js +0 -0
- package/dist/state-binding/AdapterMapping.js.map +1 -1
- package/dist/state-binding/BindingJqField.d.ts.map +1 -1
- package/dist/state-binding/BindingJqField.js +2 -2
- package/dist/state-binding/BindingJqField.js.map +1 -1
- package/dist/state-binding/InputInjectionRow.d.ts.map +1 -1
- package/dist/state-binding/InputInjectionRow.js +3 -3
- package/dist/state-binding/InputInjectionRow.js.map +1 -1
- package/dist/state-binding/StateAttachRow.d.ts.map +1 -1
- package/dist/state-binding/StateAttachRow.js +57 -41
- package/dist/state-binding/StateAttachRow.js.map +1 -1
- package/dist/state-binding/StateBindingSection.d.ts.map +1 -1
- package/dist/state-binding/StateBindingSection.js.map +1 -1
- package/dist/state-binding/SubjectScopeFields.d.ts.map +1 -1
- package/dist/state-binding/SubjectScopeFields.js.map +1 -1
- package/dist/state-binding/UpdateRow.d.ts.map +1 -1
- package/dist/state-binding/UpdateRow.js +2 -2
- package/dist/state-binding/UpdateRow.js.map +1 -1
- package/dist/state-binding/adapter-mapping-row.d.ts +17 -0
- package/dist/state-binding/adapter-mapping-row.d.ts.map +1 -0
- package/dist/state-binding/adapter-mapping-row.js +87 -0
- package/dist/state-binding/adapter-mapping-row.js.map +1 -0
- package/dist/state-binding/index.d.ts +21 -21
- package/dist/state-binding/index.d.ts.map +1 -1
- package/dist/state-binding/index.js +9 -15
- package/dist/state-binding/index.js.map +1 -1
- package/dist/state-binding/templated-text-catalog.d.ts.map +1 -1
- package/dist/state-binding/templated-text-catalog.js.map +1 -1
- package/dist/state-binding/types.d.ts +2 -2
- package/dist/state-binding/types.d.ts.map +1 -1
- package/dist/structured-output/StructuredOutput.d.ts.map +1 -1
- package/dist/structured-output/StructuredOutput.js +1 -3
- package/dist/structured-output/StructuredOutput.js.map +1 -1
- package/package.json +2 -2
- package/src/components/code-block.tsx +1 -1
- package/src/components/confirm-dialog.tsx +1 -1
- package/src/components/copy-field.tsx +1 -1
- package/src/components/date-range-picker.tsx +1 -1
- package/src/components/dialog.tsx +1 -1
- package/src/components/dirty-guard.tsx +3 -3
- package/src/components/explorer-body.tsx +239 -0
- package/src/components/explorer-page-size.ts +39 -0
- package/src/components/explorer-tag-filter.tsx +80 -0
- package/src/components/explorer-tags.ts +64 -0
- package/src/components/explorer-view.tsx +115 -367
- package/src/components/extension-combo-builder.tsx +35 -153
- package/src/components/extension-combo-parts.tsx +236 -0
- package/src/components/field.tsx +1 -1
- package/src/components/fleet-report.tsx +98 -55
- package/src/components/folder-nav.tsx +2 -2
- package/src/components/form-dialog.tsx +2 -2
- package/src/components/icons/chrome-marks.tsx +195 -0
- package/src/components/icons/icon-frame.tsx +50 -0
- package/src/components/icons/index.ts +84 -0
- package/src/components/icons/nav-map.ts +55 -0
- package/src/components/icons/nav-marks.tsx +205 -0
- package/src/components/icons/status-marks.tsx +46 -0
- package/src/components/icons/theme-marks.tsx +33 -0
- package/src/components/inputs.tsx +1 -1
- package/src/components/json-diff.tsx +1 -4
- package/src/components/json-tree-context.ts +26 -0
- package/src/components/json-tree-model.ts +139 -0
- package/src/components/json-tree-nodes.tsx +224 -0
- package/src/components/json-tree.tsx +15 -434
- package/src/components/json-value-format.ts +64 -0
- package/src/components/markdown-blocks.ts +198 -0
- package/src/components/markdown-inline.tsx +365 -0
- package/src/components/markdown.tsx +6 -472
- package/src/components/observer-registry.ts +139 -0
- package/src/components/overflow-measure.ts +69 -0
- package/src/components/page-fill.tsx +1 -1
- package/src/components/prose-regions.ts +153 -0
- package/src/components/radio-group.tsx +1 -1
- package/src/components/scroll-region.tsx +22 -361
- package/src/components/tab-stop-parse.ts +217 -0
- package/src/components/table.tsx +1 -1
- package/src/components/tags.tsx +2 -2
- package/src/components/templated-text-field.tsx +39 -332
- package/src/components/templated-text-kwargs-editor.tsx +147 -0
- package/src/components/templated-text-kwargs.ts +92 -0
- package/src/components/templated-text-source.tsx +226 -0
- package/src/components/tool-picker-parts.tsx +184 -0
- package/src/components/tool-picker.tsx +22 -114
- package/src/components/version-history-panel.tsx +39 -143
- package/src/components/version-history-rows.tsx +220 -0
- package/src/components/view-toggle.tsx +1 -1
- package/src/element-slot.ts +1 -1
- package/src/guards.ts +7 -0
- package/src/hooks/interactions-connection.ts +227 -0
- package/src/hooks/interactions-stream.ts +274 -0
- package/src/hooks/useApi.ts +1 -1
- package/src/hooks/useAuth.ts +1 -1
- package/src/hooks/useCapabilities.ts +3 -3
- package/src/hooks/useSse.ts +51 -478
- package/src/hooks/useSystemKinds.tsx +2 -2
- package/src/hooks/useTheme.ts +1 -1
- package/src/hooks/useToolDisplayNames.tsx +2 -2
- package/src/host.ts +7 -7
- package/src/index.ts +292 -292
- package/src/navigation/app-link.tsx +70 -0
- package/src/navigation/context.tsx +12 -331
- package/src/navigation/entry-state.ts +42 -0
- package/src/navigation/guard-registry.ts +65 -0
- package/src/navigation/guarded-navigation.ts +147 -0
- package/src/navigation/index.ts +14 -14
- package/src/navigation/types.ts +8 -11
- package/src/navigation/use-back-forward-guard.ts +90 -0
- package/src/navigation/use-search-commit.ts +1 -1
- package/src/plugin/host-state.ts +1 -1
- package/src/plugin/registry.ts +111 -90
- package/src/schema-editor/SchemaEditor.tsx +1 -1
- package/src/schema-editor/index.ts +3 -3
- package/src/schema-form/SecretRefField.tsx +122 -72
- package/src/schema-form/classify.ts +245 -396
- package/src/schema-form/context.ts +1 -1
- package/src/schema-form/default-value.ts +48 -17
- package/src/schema-form/enum-field.tsx +1 -1
- package/src/schema-form/field-group.tsx +1 -1
- package/src/schema-form/field-model.ts +133 -0
- package/src/schema-form/field-node.tsx +95 -39
- package/src/schema-form/index.ts +10 -10
- package/src/schema-form/json-field.tsx +1 -1
- package/src/schema-form/media-field.tsx +136 -67
- package/src/schema-form/record-field.tsx +2 -2
- package/src/schema-form/string-annotations.ts +111 -0
- package/src/schema-form/string-field.tsx +2 -2
- package/src/schema-form/union-field.tsx +3 -2
- package/src/schema-form/union.ts +1 -1
- package/src/schema-form/validate.ts +185 -104
- package/src/state-binding/AdapterMapping.tsx +0 -0
- package/src/state-binding/BindingJqField.tsx +3 -3
- package/src/state-binding/InputInjectionRow.tsx +4 -4
- package/src/state-binding/StateAttachRow.tsx +229 -154
- package/src/state-binding/StateBindingSection.tsx +1 -1
- package/src/state-binding/SubjectScopeFields.tsx +1 -1
- package/src/state-binding/UpdateRow.tsx +3 -3
- package/src/state-binding/adapter-mapping-row.tsx +232 -0
- package/src/state-binding/index.ts +43 -43
- package/src/state-binding/templated-text-catalog.ts +0 -1
- package/src/state-binding/types.ts +2 -2
- package/src/structured-output/StructuredOutput.tsx +1 -4
- package/dist/components/icons.d.ts +0 -160
- package/dist/components/icons.d.ts.map +0 -1
- package/dist/components/icons.js +0 -160
- package/dist/components/icons.js.map +0 -1
- package/src/components/icons.tsx +0 -586
package/src/hooks/useSse.ts
CHANGED
|
@@ -14,52 +14,36 @@
|
|
|
14
14
|
*
|
|
15
15
|
* The published list is `merge(seed, live)`: every seeded pending item, plus any
|
|
16
16
|
* item added on the tail since connect, minus any removed on the tail, with the
|
|
17
|
-
* client `answered` flag set on an item the tail reported answered.
|
|
17
|
+
* client `answered` flag set on an item the tail reported answered. The published
|
|
18
|
+
* `count` is the live PENDING tally, derived from the same overlay without a
|
|
19
|
+
* refetch, reconciled by EPOCH so a resync never double-counts.
|
|
18
20
|
*
|
|
19
|
-
* The
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* (answered or removed) entry is stamped with the epoch current when its frame
|
|
26
|
-
* arrived. The count
|
|
27
|
-
* is the seed `total` plus live adds not in the seed AND stamped at or after
|
|
28
|
-
* `countEpoch`, minus one for every terminal id stamped at or after `countEpoch`
|
|
29
|
-
* (deduped per id) — a terminal or an add already folded into the refetched `total`
|
|
30
|
-
* carries an older stamp and is not applied again. The single residual: a frame
|
|
31
|
-
* arriving while a refetch is in flight may or may not be reflected in that refetch's
|
|
32
|
-
* `total`, and is reconciled on the next resync.
|
|
33
|
-
*
|
|
34
|
-
* ANSWERED-CARD LIFECYCLE: the server emits no removal for an answered card —
|
|
35
|
-
* only `interaction.answered`, never `interaction.removed` for it — and the paged
|
|
36
|
-
* base never carries answered items. On its answered frame a card is promoted into
|
|
37
|
-
* the overlay's adds (a seed-origin card too), so it lingers uniformly whether it
|
|
38
|
-
* began as a live add or a seed item, even after a refetch drops it from the base.
|
|
39
|
-
* This hook AGES it out itself: an answered card is dropped `ANSWERED_RETENTION_MS`
|
|
40
|
-
* after its answered frame (tombstoned so a still-stale seed cannot resurrect it),
|
|
41
|
-
* with `ANSWERED_CAP` as a hard newest-N flood backstop. Both are swept lazily on
|
|
42
|
-
* frame/reconnect ticks (the stream carries no idle heartbeat frame), never a
|
|
43
|
-
* per-item timer.
|
|
44
|
-
*
|
|
45
|
-
* `add` / `answered` frames are at-least-once; entries are keyed by
|
|
46
|
-
* `interaction_id`, a redelivered `add` overwrites in place, and a redelivered
|
|
47
|
-
* `answered` keeps the original stamp so the retention window is not extended.
|
|
21
|
+
* The pure frame parsing, overlay reducer and derived projections live in
|
|
22
|
+
* `interactions-stream`; the fetch/reconnect/resync loop lives in
|
|
23
|
+
* `interactions-connection`. This hook holds the React state those two operate on
|
|
24
|
+
* and wires them together: the overlay is a ref (mutated in place by the driver),
|
|
25
|
+
* the published list is `merge(seed, overlay)` held in state and recomputed on
|
|
26
|
+
* every overlay change and every seed change, and the count is derived on render.
|
|
48
27
|
*
|
|
49
28
|
* The `Interaction` type is imported type-only (no runtime dep on api-client);
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* client flag this hook flips on an `interaction.answered` event.
|
|
29
|
+
* `answered` is not a wire field — it is a client flag flipped on an
|
|
30
|
+
* `interaction.answered` event.
|
|
53
31
|
*/
|
|
54
|
-
import { useEffect, useRef, useState } from 'react';
|
|
55
32
|
import type { Interaction } from '@tai42/api-client';
|
|
33
|
+
import { useEffect, useRef, useState } from 'react';
|
|
56
34
|
|
|
35
|
+
import { type ConnectionState, startInteractionsStream } from './interactions-connection';
|
|
36
|
+
import {
|
|
37
|
+
emptyOverlay,
|
|
38
|
+
type LiveOverlay,
|
|
39
|
+
merge,
|
|
40
|
+
pendingCount,
|
|
41
|
+
type StreamInteraction,
|
|
42
|
+
} from './interactions-stream';
|
|
57
43
|
import { useApi } from './useApi';
|
|
58
44
|
import { useOnUnauthorized } from './useUnauthorized';
|
|
59
|
-
import { isFeatureDisabled } from '../feature-disabled';
|
|
60
45
|
|
|
61
|
-
|
|
62
|
-
export type StreamInteraction = Interaction & { readonly answered: boolean };
|
|
46
|
+
export type { StreamInteraction } from './interactions-stream';
|
|
63
47
|
|
|
64
48
|
/** The inputs the caller drives the tail-only stream with. */
|
|
65
49
|
export interface InteractionsStreamOptions {
|
|
@@ -102,259 +86,34 @@ export interface InteractionsStreamState {
|
|
|
102
86
|
/**
|
|
103
87
|
* The interactions store is not configured on this deployment: the stream
|
|
104
88
|
* answered with a terminal 501 `interactions-not-configured`. Reconnection is
|
|
105
|
-
* abandoned
|
|
106
|
-
* renders the muted OFF state instead of a red error or a floating badge.
|
|
89
|
+
* abandoned and the consumer renders the muted OFF state instead of a red error.
|
|
107
90
|
*/
|
|
108
91
|
readonly disabled: boolean;
|
|
109
92
|
}
|
|
110
93
|
|
|
111
|
-
// Reconnect backoff: capped exponential with full jitter. The delay for attempt
|
|
112
|
-
// n is a random value in [0, min(CAP, BASE * 2**n)); the attempt counter resets
|
|
113
|
-
// only when a connection PROVES HEALTHY — it stayed open at least
|
|
114
|
-
// `HEALTHY_CONNECTION_MS` — not merely when it opens, so a server that accepts the
|
|
115
|
-
// request then drops the body still backs off instead of tight-looping. A healthy
|
|
116
|
-
// tail-reconnect is near-instant while a sustained outage backs off, and the
|
|
117
|
-
// jitter spreads many clients apart rather than retrying in lockstep.
|
|
118
|
-
const RECONNECT_BASE_MS = 1500;
|
|
119
|
-
const RECONNECT_CAP_MS = 30000;
|
|
120
|
-
const HEALTHY_CONNECTION_MS = RECONNECT_BASE_MS;
|
|
121
|
-
|
|
122
|
-
// The server emits no removal for an answered card — only `interaction.answered`,
|
|
123
|
-
// never `interaction.removed` for it — and the paged base never carries answered
|
|
124
|
-
// items, so answered cards would otherwise pile up unbounded in the always-mounted
|
|
125
|
-
// badge/inbox. The client ages them out: drop an answered card
|
|
126
|
-
// ANSWERED_RETENTION_MS after its answered frame, keeping at most ANSWERED_CAP as a
|
|
127
|
-
// hard flood backstop. Swept lazily on frame/reconnect ticks (the stream carries no
|
|
128
|
-
// idle heartbeat frame), never a per-item timer.
|
|
129
|
-
const ANSWERED_RETENTION_MS = 10 * 60 * 1000;
|
|
130
|
-
const ANSWERED_CAP = 200;
|
|
131
|
-
|
|
132
|
-
// The wire `answer_format` values. Duplicated from the api-client enum by design:
|
|
133
|
-
// the SDK is the leaf and must not import api-client at runtime. The `satisfies`
|
|
134
|
-
// pin against `Interaction['answer_format']` fails to compile if a listed value
|
|
135
|
-
// leaves the union, so this runtime guard cannot silently drift from the type.
|
|
136
|
-
const ANSWER_FORMATS = new Set<string>([
|
|
137
|
-
'text',
|
|
138
|
-
'confirm',
|
|
139
|
-
'select',
|
|
140
|
-
'form',
|
|
141
|
-
'external',
|
|
142
|
-
] as const satisfies readonly Interaction['answer_format'][]);
|
|
143
|
-
|
|
144
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
145
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** Parse a frame's JSON to a plain object, or null when it is not one. */
|
|
149
|
-
function parseJson(data: string): Record<string, unknown> | null {
|
|
150
|
-
if (!data) return null;
|
|
151
|
-
try {
|
|
152
|
-
const obj: unknown = JSON.parse(data);
|
|
153
|
-
return isRecord(obj) ? obj : null;
|
|
154
|
-
} catch {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Validate an `interaction.add` frame into an `Interaction`, or null when it is
|
|
161
|
-
* malformed. This mirrors the api-client `interaction` zod schema exactly — it
|
|
162
|
-
* accepts what that schema accepts, no more, no less. The strict-required fields
|
|
163
|
-
* — `interaction_id`, `group_id`, `created_at`, `timeout_at` — must be a string
|
|
164
|
-
* and `answer_format` a known enum value; any missing or non-string one returns
|
|
165
|
-
* null so the caller surfaces it as an error rather than rendering a blank card.
|
|
166
|
-
* Two fields are laxer, matching the schema: `question` is
|
|
167
|
-
* `z.string().default('')`, whose default substitutes ONLY an absent/undefined
|
|
168
|
-
* value, so a missing question normalizes to `''` while a present `null` (or any
|
|
169
|
-
* present non-string) is rejected; `format_payload` is nullish, so an absent/null
|
|
170
|
-
* value (text/confirm carry none) normalizes to `{}` while a present non-object is
|
|
171
|
-
* rejected — never silently coerced to a blank card.
|
|
172
|
-
*/
|
|
173
|
-
function parseAddFrame(data: string): Interaction | null {
|
|
174
|
-
const obj = parseJson(data);
|
|
175
|
-
if (obj === null) return null;
|
|
176
|
-
const {
|
|
177
|
-
interaction_id,
|
|
178
|
-
group_id,
|
|
179
|
-
question,
|
|
180
|
-
answer_format,
|
|
181
|
-
format_payload,
|
|
182
|
-
created_at,
|
|
183
|
-
timeout_at,
|
|
184
|
-
sensitive,
|
|
185
|
-
server_verified,
|
|
186
|
-
channel,
|
|
187
|
-
recipient,
|
|
188
|
-
origin,
|
|
189
|
-
audience,
|
|
190
|
-
media,
|
|
191
|
-
} = obj;
|
|
192
|
-
if (typeof interaction_id !== 'string') return null;
|
|
193
|
-
if (typeof group_id !== 'string') return null;
|
|
194
|
-
// `question` is z.string().default('') in the schema: the default substitutes
|
|
195
|
-
// ONLY an absent/undefined value, so a missing question is valid while a
|
|
196
|
-
// present null (or any other present non-string) is malformed.
|
|
197
|
-
if (question !== undefined && typeof question !== 'string') return null;
|
|
198
|
-
if (typeof created_at !== 'string') return null;
|
|
199
|
-
if (typeof timeout_at !== 'string') return null;
|
|
200
|
-
if (typeof answer_format !== 'string' || !ANSWER_FORMATS.has(answer_format)) return null;
|
|
201
|
-
// `format_payload` is nullish in the schema: absent/null normalizes to {}, but
|
|
202
|
-
// a present non-object is malformed and must not be coerced to a blank card.
|
|
203
|
-
if (format_payload != null && !isRecord(format_payload)) return null;
|
|
204
|
-
// `sensitive` is z.boolean().default(false): an absent value normalizes to
|
|
205
|
-
// false, while a present non-boolean is malformed.
|
|
206
|
-
if (sensitive !== undefined && typeof sensitive !== 'boolean') return null;
|
|
207
|
-
// `server_verified` is z.boolean().optional(): an absent value stays undefined,
|
|
208
|
-
// while a present non-boolean is malformed.
|
|
209
|
-
if (server_verified !== undefined && typeof server_verified !== 'boolean') return null;
|
|
210
|
-
// `channel` is z.string().optional(): the delivery-channel name rides the frame
|
|
211
|
-
// only for a channel-delivered question; absent stays undefined, a present
|
|
212
|
-
// non-string is malformed.
|
|
213
|
-
if (channel !== undefined && typeof channel !== 'string') return null;
|
|
214
|
-
// Attribution fields are each z.string().optional(): `recipient` (delivery
|
|
215
|
-
// address), `origin` (asking run id), `audience` (addressed user_id) ride the
|
|
216
|
-
// frame only when set; absent stays undefined, a present non-string is malformed.
|
|
217
|
-
if (recipient !== undefined && typeof recipient !== 'string') return null;
|
|
218
|
-
if (origin !== undefined && typeof origin !== 'string') return null;
|
|
219
|
-
if (audience !== undefined && typeof audience !== 'string') return null;
|
|
220
|
-
// `media` is z.array(z.unknown()).optional(): the display-only media rides the
|
|
221
|
-
// frame only when the question carries some; absent stays undefined, a present
|
|
222
|
-
// non-array is malformed. The ITEMS stay unknown here (loose by design) — each is
|
|
223
|
-
// validated per item by the renderer, so one bad item never fails this frame.
|
|
224
|
-
if (media !== undefined && !Array.isArray(media)) return null;
|
|
225
|
-
return {
|
|
226
|
-
interaction_id,
|
|
227
|
-
group_id,
|
|
228
|
-
question: typeof question === 'string' ? question : '',
|
|
229
|
-
answer_format: answer_format as Interaction['answer_format'],
|
|
230
|
-
format_payload: isRecord(format_payload) ? format_payload : {},
|
|
231
|
-
created_at,
|
|
232
|
-
timeout_at,
|
|
233
|
-
sensitive: sensitive ?? false,
|
|
234
|
-
...(typeof server_verified === 'boolean' ? { server_verified } : {}),
|
|
235
|
-
...(typeof channel === 'string' ? { channel } : {}),
|
|
236
|
-
...(typeof recipient === 'string' ? { recipient } : {}),
|
|
237
|
-
...(typeof origin === 'string' ? { origin } : {}),
|
|
238
|
-
...(typeof audience === 'string' ? { audience } : {}),
|
|
239
|
-
...(Array.isArray(media) ? { media } : {}),
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/** The interaction id from an `answered` / `removed` frame, or null. */
|
|
244
|
-
function parseId(data: string): string | null {
|
|
245
|
-
const id = parseJson(data)?.interaction_id;
|
|
246
|
-
return typeof id === 'string' ? id : null;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* The live overlay a connection accumulates over the paged base: the tail's own
|
|
251
|
-
* adds (kept whether or not the base already lists them, so a fresher add wins),
|
|
252
|
-
* the answered stamps (wall-clock ms, for the aging sweep), and the tombstones for
|
|
253
|
-
* ids removed on the tail OR aged out of the answered window (a tombstone stops a
|
|
254
|
-
* still-stale seed from re-showing a gone card; ids are never reused). `addEpoch`
|
|
255
|
-
* and `goneEpoch` carry the resync epoch current when an add / a first terminal frame
|
|
256
|
-
* arrived, so the count applies an add (or subtracts a terminal) only while it is not
|
|
257
|
-
* yet folded into the refetched `total`; the merge and the aging sweep ignore them.
|
|
258
|
-
*/
|
|
259
|
-
interface LiveOverlay {
|
|
260
|
-
readonly adds: Map<string, Interaction>;
|
|
261
|
-
readonly addEpoch: Map<string, number>;
|
|
262
|
-
readonly answeredAt: Map<string, number>;
|
|
263
|
-
readonly removed: Set<string>;
|
|
264
|
-
readonly goneEpoch: Map<string, number>;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function emptyOverlay(): LiveOverlay {
|
|
268
|
-
return {
|
|
269
|
-
adds: new Map(),
|
|
270
|
-
addEpoch: new Map(),
|
|
271
|
-
answeredAt: new Map(),
|
|
272
|
-
removed: new Set(),
|
|
273
|
-
goneEpoch: new Map(),
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/** Fold the paged base and the live overlay into the published, ordered list. */
|
|
278
|
-
function merge(seed: readonly Interaction[], live: LiveOverlay): StreamInteraction[] {
|
|
279
|
-
const out = new Map<string, StreamInteraction>();
|
|
280
|
-
for (const item of seed) {
|
|
281
|
-
if (live.removed.has(item.interaction_id)) continue;
|
|
282
|
-
out.set(item.interaction_id, { ...item, answered: live.answeredAt.has(item.interaction_id) });
|
|
283
|
-
}
|
|
284
|
-
for (const [id, item] of live.adds) {
|
|
285
|
-
if (live.removed.has(id)) continue;
|
|
286
|
-
out.set(id, { ...item, answered: live.answeredAt.has(id) });
|
|
287
|
-
}
|
|
288
|
-
return Array.from(out.values());
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* The live pending tally: the door's `total` for the seed, plus live adds whose id
|
|
293
|
-
* is absent from the seed AND stamped at or after `countEpoch` (a new question not
|
|
294
|
-
* yet folded into `total`), minus one for every terminal id — answered OR removed —
|
|
295
|
-
* stamped at or after `countEpoch`, deduped per id so an id counts once however many
|
|
296
|
-
* terminal frames it drew. `countEpoch` is the epoch of the last resync whose refetch
|
|
297
|
-
* LANDED (a failed refetch never advances it): a delta stamped before it is already
|
|
298
|
-
* reflected in the fresh `total`, so it is NOT applied again — this is what stops a
|
|
299
|
-
* resync from re-subtracting a terminal it already dropped from `total`, while a
|
|
300
|
-
* failed refetch keeps the prior-epoch deltas applying against the prior total. Every
|
|
301
|
-
* terminal frame a client receives refers to a
|
|
302
|
-
* question inside its own `total` (the stream and the list door apply the same
|
|
303
|
-
* audience filter). One residual race remains: a frame arriving while a refetch is in
|
|
304
|
-
* flight may already be reflected in that refetch's `total` (the resync GET landing
|
|
305
|
-
* after an answer POST), so the raw tally can transiently dip below zero until the
|
|
306
|
-
* next resync reconciles — observed live as a "-1 pending questions" badge. A
|
|
307
|
-
* pending tally below zero is meaningless, so the published count clamps at 0. The
|
|
308
|
-
* clamp covers only the negative symptom: the same race can also leave a transient
|
|
309
|
-
* positive under-count (a double-subtracted question hiding a still-pending one),
|
|
310
|
-
* which reconciles on the next resync, not here.
|
|
311
|
-
*/
|
|
312
|
-
function pendingCount(
|
|
313
|
-
seed: readonly Interaction[],
|
|
314
|
-
total: number,
|
|
315
|
-
live: LiveOverlay,
|
|
316
|
-
countEpoch: number,
|
|
317
|
-
): number {
|
|
318
|
-
const seedIds = new Set(seed.map((item) => item.interaction_id));
|
|
319
|
-
let count = total;
|
|
320
|
-
for (const id of live.adds.keys()) {
|
|
321
|
-
if (!seedIds.has(id) && (live.addEpoch.get(id) ?? 0) >= countEpoch) count += 1;
|
|
322
|
-
}
|
|
323
|
-
const gone = new Set<string>([...live.answeredAt.keys(), ...live.removed]);
|
|
324
|
-
for (const id of gone) {
|
|
325
|
-
if ((live.goneEpoch.get(id) ?? 0) >= countEpoch) count -= 1;
|
|
326
|
-
}
|
|
327
|
-
return Math.max(count, 0);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
94
|
export function useInteractionsStream(options: InteractionsStreamOptions): InteractionsStreamState {
|
|
331
95
|
const { seed, total, onResync } = options;
|
|
332
96
|
const api = useApi();
|
|
333
97
|
const onUnauthorized = useOnUnauthorized();
|
|
334
98
|
|
|
335
|
-
const [connectionState, setConnectionState] = useState<{
|
|
336
|
-
connected:
|
|
337
|
-
error:
|
|
338
|
-
disabled:
|
|
339
|
-
}
|
|
99
|
+
const [connectionState, setConnectionState] = useState<ConnectionState>({
|
|
100
|
+
connected: false,
|
|
101
|
+
error: null,
|
|
102
|
+
disabled: false,
|
|
103
|
+
});
|
|
340
104
|
|
|
341
|
-
// The live overlay is a ref (mutated in place by the
|
|
342
|
-
// list is `merge(seed, overlay)` held in state, recomputed on every
|
|
343
|
-
// change (the
|
|
105
|
+
// The live overlay is a ref (mutated in place by the connection driver); the
|
|
106
|
+
// published list is `merge(seed, overlay)` held in state, recomputed on every
|
|
107
|
+
// overlay change (the driver) and every seed change (a refetch of the paged base).
|
|
344
108
|
const overlayRef = useRef<LiveOverlay>(emptyOverlay());
|
|
345
109
|
const [interactions, setInteractions] = useState<StreamInteraction[]>(() =>
|
|
346
110
|
merge(seed, overlayRef.current),
|
|
347
111
|
);
|
|
348
112
|
|
|
349
|
-
// Resync epochs. `epochRef` increments on each (re)connect and stamps every frame
|
|
350
|
-
//
|
|
351
|
-
// once its refetch landed fresh (onResync resolved true) — a failed refetch leaves
|
|
352
|
-
// it unchanged so prior-epoch deltas keep applying against the prior total.
|
|
353
|
-
// `pendingCount` applies only overlay entries stamped at or after `countEpoch`, so a
|
|
354
|
-
// resync never re-subtracts a terminal it already dropped from the fresh `total`.
|
|
113
|
+
// Resync epochs. `epochRef` increments on each (re)connect and stamps every frame;
|
|
114
|
+
// `countEpoch` advances to a connect's epoch only once its refetch landed fresh.
|
|
355
115
|
// `countEpoch` is STATE, not a ref: advancing it must republish the derived count
|
|
356
|
-
// even when the reconnect carries no frame after the refetch.
|
|
357
|
-
// overlay on remount.
|
|
116
|
+
// even when the reconnect carries no frame after the refetch.
|
|
358
117
|
const epochRef = useRef(0);
|
|
359
118
|
const [countEpoch, setCountEpoch] = useState(0);
|
|
360
119
|
|
|
@@ -370,211 +129,25 @@ export function useInteractionsStream(options: InteractionsStreamOptions): Inter
|
|
|
370
129
|
setInteractions(merge(seed, overlayRef.current));
|
|
371
130
|
}, [seed]);
|
|
372
131
|
|
|
373
|
-
useEffect(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
// resurrect a card the client has retired.
|
|
389
|
-
const sweepAnswered = () => {
|
|
390
|
-
const now = Date.now();
|
|
391
|
-
const retire = (id: string) => {
|
|
392
|
-
overlay.answeredAt.delete(id);
|
|
393
|
-
overlay.adds.delete(id);
|
|
394
|
-
overlay.addEpoch.delete(id);
|
|
395
|
-
overlay.removed.add(id);
|
|
396
|
-
// The id keeps its original `goneEpoch` (stamped on its answered frame) so
|
|
397
|
-
// moving answered→removed never re-dates it into a fresher resync.
|
|
398
|
-
};
|
|
399
|
-
for (const [id, at] of overlay.answeredAt) {
|
|
400
|
-
if (now - at >= ANSWERED_RETENTION_MS) retire(id);
|
|
401
|
-
}
|
|
402
|
-
if (overlay.answeredAt.size > ANSWERED_CAP) {
|
|
403
|
-
const oldest = [...overlay.answeredAt.entries()].sort((a, b) => a[1] - b[1]);
|
|
404
|
-
for (const [id] of oldest.slice(0, oldest.length - ANSWERED_CAP)) retire(id);
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
const surfaceMalformed = (event: string) => {
|
|
409
|
-
setConnectionState((prev) => ({
|
|
410
|
-
...prev,
|
|
411
|
-
error: new Error(`malformed interaction frame: ${event}`),
|
|
412
|
-
}));
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
// A successful data frame sweeps, republishes the merged list, and clears any
|
|
416
|
-
// transient frame-parse error — one bad frame on a healthy stream must not
|
|
417
|
-
// stick. It does NOT refetch: the overlay already updated the list and the
|
|
418
|
-
// derived count, and `onResync` fires on (re)connect only.
|
|
419
|
-
const commit = () => {
|
|
420
|
-
if (aborted()) return;
|
|
421
|
-
sweepAnswered();
|
|
422
|
-
setConnectionState((prev) => (prev.error === null ? prev : { ...prev, error: null }));
|
|
423
|
-
setInteractions(merge(seedRef.current, overlay));
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
const applyFrame = (event: string, data: string) => {
|
|
427
|
-
if (event === 'interaction.removed') {
|
|
428
|
-
const id = parseId(data);
|
|
429
|
-
if (id === null) {
|
|
430
|
-
surfaceMalformed(event);
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
overlay.adds.delete(id);
|
|
434
|
-
overlay.addEpoch.delete(id);
|
|
435
|
-
overlay.answeredAt.delete(id);
|
|
436
|
-
overlay.removed.add(id);
|
|
437
|
-
// Stamp the terminal epoch on the FIRST terminal frame for this id only.
|
|
438
|
-
if (!overlay.goneEpoch.has(id)) overlay.goneEpoch.set(id, epochRef.current);
|
|
439
|
-
commit();
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
if (event === 'interaction.answered') {
|
|
443
|
-
const id = parseId(data);
|
|
444
|
-
if (id === null) {
|
|
445
|
-
surfaceMalformed(event);
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
// Stamp the answered time on the FIRST answered frame only; a redelivered
|
|
449
|
-
// answered keeps the original stamp so the retention window is not extended.
|
|
450
|
-
if (!overlay.answeredAt.has(id)) overlay.answeredAt.set(id, Date.now());
|
|
451
|
-
// Stamp the terminal epoch on the FIRST terminal frame for this id only, so a
|
|
452
|
-
// later terminal never re-dates it into a fresher resync.
|
|
453
|
-
if (!overlay.goneEpoch.has(id)) overlay.goneEpoch.set(id, epochRef.current);
|
|
454
|
-
// Promote a seed-origin card into the overlay so it lingers uniformly with a
|
|
455
|
-
// live-add card: the paged base never carries answered items, so a refetch
|
|
456
|
-
// would otherwise drop it before its retention window elapses.
|
|
457
|
-
if (!overlay.adds.has(id)) {
|
|
458
|
-
const seeded = seedRef.current.find((item) => item.interaction_id === id);
|
|
459
|
-
if (seeded !== undefined) {
|
|
460
|
-
overlay.adds.set(id, seeded);
|
|
461
|
-
overlay.addEpoch.set(id, epochRef.current);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
commit();
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
// interaction.add — the full question. Ids are never reused, so a live add
|
|
468
|
-
// simply overwrites any prior copy in place.
|
|
469
|
-
const interaction = parseAddFrame(data);
|
|
470
|
-
if (interaction === null) {
|
|
471
|
-
surfaceMalformed(event);
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
overlay.adds.set(interaction.interaction_id, interaction);
|
|
475
|
-
overlay.addEpoch.set(interaction.interaction_id, epochRef.current);
|
|
476
|
-
commit();
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
// The abort signal is the single cancellation source of truth: the cleanup
|
|
480
|
-
// aborts it, and every loop guard reads it fresh.
|
|
481
|
-
const run = async () => {
|
|
482
|
-
while (!aborted()) {
|
|
483
|
-
const openedAt = Date.now();
|
|
484
|
-
try {
|
|
485
|
-
const frames = await api.streamInteractions(controller.signal, lastEventId);
|
|
486
|
-
if (aborted()) return;
|
|
487
|
-
setConnectionState((prev) => ({
|
|
488
|
-
...prev,
|
|
489
|
-
connected: true,
|
|
490
|
-
error: null,
|
|
491
|
-
disabled: false,
|
|
492
|
-
}));
|
|
493
|
-
// Tail-only: the pending base is not on the stream. Refetch it on every
|
|
494
|
-
// (re)connect under a fresh epoch so anything removed or added while
|
|
495
|
-
// disconnected is reconciled by the fresh page; record that epoch as
|
|
496
|
-
// `countEpoch` only once the refetch LANDED (onResync resolved true), so the
|
|
497
|
-
// count applies only deltas not yet folded into the fresh `total`. A failed
|
|
498
|
-
// refetch (false) leaves `countEpoch` unchanged, so prior-epoch deltas keep
|
|
499
|
-
// applying against the consistent prior total instead of being discarded
|
|
500
|
-
// against a stale one. Every frame on this connection is stamped with `epoch`.
|
|
501
|
-
const epoch = (epochRef.current += 1);
|
|
502
|
-
const landed = await onResyncRef.current();
|
|
503
|
-
if (aborted()) return;
|
|
504
|
-
if (landed) setCountEpoch(epoch);
|
|
505
|
-
for await (const frame of frames) {
|
|
506
|
-
if (aborted()) return;
|
|
507
|
-
applyFrame(frame.event, frame.data);
|
|
508
|
-
// Advance the resume cursor to every id-bearing frame (even a malformed
|
|
509
|
-
// one) so a reconnect never re-requests a frame already delivered.
|
|
510
|
-
if (frame.id !== undefined) lastEventId = frame.id;
|
|
511
|
-
}
|
|
512
|
-
} catch (err) {
|
|
513
|
-
if (aborted()) return;
|
|
514
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
515
|
-
setConnectionState((prev) => ({ ...prev, connected: false, error }));
|
|
516
|
-
if (error.name === 'ApiUnauthorizedError') {
|
|
517
|
-
// A 401 means the stored credential is dead — reconnecting would just
|
|
518
|
-
// replay the same bad key every delay forever. Treat it as terminal
|
|
519
|
-
// and hand off to the app's 401→login handler, the same path every
|
|
520
|
-
// other data call takes; do not loop.
|
|
521
|
-
onUnauthorized();
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
if (isFeatureDisabled(error)) {
|
|
525
|
-
// A terminal 501 `interactions-not-configured`: the deployment runs no
|
|
526
|
-
// interactions store, so reconnecting would replay the same refusal on
|
|
527
|
-
// every backoff forever (this stream is always-mounted via the shell
|
|
528
|
-
// badge). Stop, and flag the OFF state so the badge disappears and the
|
|
529
|
-
// page renders the muted "not configured" note instead of a red error.
|
|
530
|
-
setConnectionState((prev) => ({ ...prev, connected: false, disabled: true }));
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
// A connection that stayed open at least HEALTHY_CONNECTION_MS proved
|
|
535
|
-
// healthy — clear the reconnect backoff so the next drop reconnects
|
|
536
|
-
// promptly; an immediate accept-then-drop flap does not reset it.
|
|
537
|
-
if (Date.now() - openedAt >= HEALTHY_CONNECTION_MS) reconnectAttempt = 0;
|
|
538
|
-
// Stream ended or errored → wait, then reconnect. Guard before the
|
|
539
|
-
// setState, like every other setState in run(): a microtask scheduled after
|
|
540
|
-
// the `for await` must not write state after the effect cleanup has aborted
|
|
541
|
-
// (and unmounted) the hook.
|
|
542
|
-
if (aborted()) return;
|
|
543
|
-
setConnectionState((prev) => ({ ...prev, connected: false }));
|
|
544
|
-
const ceiling = Math.min(RECONNECT_CAP_MS, RECONNECT_BASE_MS * 2 ** reconnectAttempt);
|
|
545
|
-
const delay = Math.random() * ceiling;
|
|
546
|
-
reconnectAttempt += 1;
|
|
547
|
-
await new Promise<void>((resolve) => {
|
|
548
|
-
const onAbort = () => {
|
|
549
|
-
clearTimeout(timer);
|
|
550
|
-
resolve();
|
|
551
|
-
};
|
|
552
|
-
const timer = setTimeout(() => {
|
|
553
|
-
// Normal wake: drop the abort listener so it does not accumulate
|
|
554
|
-
// across reconnects ({ once: true } only self-removes on firing).
|
|
555
|
-
controller.signal.removeEventListener('abort', onAbort);
|
|
556
|
-
resolve();
|
|
557
|
-
}, delay);
|
|
558
|
-
controller.signal.addEventListener('abort', onAbort, { once: true });
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
void run();
|
|
564
|
-
return () => {
|
|
565
|
-
controller.abort();
|
|
566
|
-
// A fresh mount starts from an empty overlay at epoch 0: the effect owns the
|
|
567
|
-
// connection and its live deltas, so a re-run must not inherit the prior run's
|
|
568
|
-
// tail or its epoch stamps.
|
|
569
|
-
overlayRef.current = emptyOverlay();
|
|
570
|
-
epochRef.current = 0;
|
|
571
|
-
setCountEpoch(0);
|
|
572
|
-
};
|
|
573
|
-
}, [api, onUnauthorized]);
|
|
132
|
+
useEffect(
|
|
133
|
+
() =>
|
|
134
|
+
startInteractionsStream({
|
|
135
|
+
api,
|
|
136
|
+
overlayRef,
|
|
137
|
+
epochRef,
|
|
138
|
+
seedRef,
|
|
139
|
+
onResyncRef,
|
|
140
|
+
onUnauthorized,
|
|
141
|
+
setConnectionState,
|
|
142
|
+
setInteractions,
|
|
143
|
+
setCountEpoch,
|
|
144
|
+
}),
|
|
145
|
+
[api, onUnauthorized],
|
|
146
|
+
);
|
|
574
147
|
|
|
575
|
-
// Derived live, not stored: every overlay mutation republishes `interactions
|
|
576
|
-
//
|
|
577
|
-
//
|
|
148
|
+
// Derived live, not stored: every overlay mutation republishes `interactions`, and
|
|
149
|
+
// every seed/total change and every `countEpoch` advance is itself a render, so
|
|
150
|
+
// this recomputes from the current overlay ref on the same render.
|
|
578
151
|
const count = pendingCount(seed, total, overlayRef.current, countEpoch);
|
|
579
152
|
|
|
580
153
|
return {
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
* as not-off until the table is `ready`, and a failed fetch leaves every kind not-off
|
|
17
17
|
* so the surface falls back to its reactive 501 handling, never a hard-failed page.
|
|
18
18
|
*/
|
|
19
|
+
import type { KindStatus } from '@tai42/api-client';
|
|
19
20
|
import {
|
|
20
21
|
createContext,
|
|
21
22
|
createElement,
|
|
23
|
+
type ReactNode,
|
|
22
24
|
useContext,
|
|
23
25
|
useEffect,
|
|
24
26
|
useState,
|
|
25
|
-
type ReactNode,
|
|
26
27
|
} from 'react';
|
|
27
|
-
import type { KindStatus } from '@tai42/api-client';
|
|
28
28
|
|
|
29
29
|
import { useApi } from './useApi';
|
|
30
30
|
import { useAuth } from './useAuth';
|
package/src/hooks/useTheme.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* storage untouched. A `'system'` preference clears any pinned choice and hands
|
|
14
14
|
* the theme back to the OS.
|
|
15
15
|
*/
|
|
16
|
+
import type { ReactNode } from 'react';
|
|
16
17
|
import {
|
|
17
18
|
createContext,
|
|
18
19
|
createElement,
|
|
@@ -22,7 +23,6 @@ import {
|
|
|
22
23
|
useMemo,
|
|
23
24
|
useState,
|
|
24
25
|
} from 'react';
|
|
25
|
-
import type { ReactNode } from 'react';
|
|
26
26
|
|
|
27
27
|
export type Theme = 'light' | 'dark';
|
|
28
28
|
export type ThemePreference = 'light' | 'dark' | 'system';
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
* display name is cosmetic: a picker with an empty map falls back to the bare raw
|
|
13
13
|
* name, never a hard-failed page.
|
|
14
14
|
*/
|
|
15
|
+
import type { ToolMetaOverlay } from '@tai42/api-client';
|
|
15
16
|
import {
|
|
16
17
|
createContext,
|
|
17
18
|
createElement,
|
|
19
|
+
type ReactNode,
|
|
18
20
|
useCallback,
|
|
19
21
|
useContext,
|
|
20
22
|
useEffect,
|
|
21
23
|
useMemo,
|
|
22
24
|
useState,
|
|
23
|
-
type ReactNode,
|
|
24
25
|
} from 'react';
|
|
25
|
-
import type { ToolMetaOverlay } from '@tai42/api-client';
|
|
26
26
|
|
|
27
27
|
import { useApi } from './useApi';
|
|
28
28
|
import { useAuth } from './useAuth';
|
package/src/host.ts
CHANGED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
* carries no way to forge, wipe, or enumerate the registry. Only the host bundle
|
|
6
6
|
* imports this. See SECURITY.md for the trust boundary.
|
|
7
7
|
*/
|
|
8
|
-
export {
|
|
8
|
+
export type {
|
|
9
|
+
LoadedPlugin,
|
|
10
|
+
PluginContributionsSnapshot,
|
|
11
|
+
PluginLoaderState,
|
|
12
|
+
} from './plugin/host-state';
|
|
9
13
|
export {
|
|
10
|
-
setPluginHostState,
|
|
11
14
|
getPluginHostState,
|
|
15
|
+
setPluginHostState,
|
|
12
16
|
subscribePluginHost,
|
|
13
17
|
usePluginContributions,
|
|
14
18
|
} from './plugin/host-state';
|
|
15
|
-
export
|
|
16
|
-
PluginLoaderState,
|
|
17
|
-
LoadedPlugin,
|
|
18
|
-
PluginContributionsSnapshot,
|
|
19
|
-
} from './plugin/host-state';
|
|
19
|
+
export { getContributions, loadPlugin } from './plugin/registry';
|