akanjs 3.1.0-rc.2 → 3.1.0-rc.21
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/base/baseEnv.ts +5 -2
- package/client/device.ts +1 -1
- package/client/frameConfig.ts +2 -2
- package/client/href.ts +35 -0
- package/client/index.ts +1 -0
- package/client/makePageProto.tsx +3 -2
- package/client/router.ts +6 -4
- package/client/rscNavigation.ts +1 -1
- package/client/transitionTypes.ts +0 -4
- package/client/translator.ts +16 -0
- package/client/types.ts +3 -2
- package/client/viewportMeta.ts +23 -8
- package/client/webManifest.ts +5 -3
- package/common/TrustedProxy.ts +14 -0
- package/common/codeAgentWire.ts +8 -1
- package/common/index.ts +1 -0
- package/common/interpolateTranslation.ts +17 -3
- package/common/isEmail.ts +1 -1
- package/common/localeConfig.ts +10 -4
- package/common/markdownSpans.ts +1 -1
- package/common/restClient.ts +3 -1
- package/common/routeConvention.ts +4 -4
- package/common/trimSlashes.ts +11 -0
- package/common/types.ts +1 -1
- package/constant/fieldInfo.ts +2 -2
- package/constant/fieldPlan.ts +80 -0
- package/constant/index.ts +3 -0
- package/constant/inputParser.ts +148 -0
- package/constant/invalidInputError.ts +13 -0
- package/constant/serialize.ts +36 -20
- package/constant/types.ts +1 -1
- package/constant/via.ts +2 -0
- package/dictionary/base.dictionary.ts +4 -0
- package/dictionary/trans.ts +8 -0
- package/document/database.ts +3 -6
- package/document/documentSchema.ts +1 -1
- package/document/noDocumentError.ts +6 -1
- package/fetch/client/fetchClient.ts +2 -1
- package/fetch/client/sliceInitHandle.ts +2 -3
- package/fetch/fetchType/appliedReturn.type.ts +3 -1
- package/fetch/types.ts +4 -2
- package/index.ts +2 -0
- package/package.json +31 -21
- package/server/akanApp.ts +15 -3
- package/server/akanServer.ts +34 -14
- package/server/artifact/routeClientCache.ts +4 -5
- package/server/artifact/routeSeedIndexStore.ts +3 -20
- package/server/artifact/routeSeedMatcher.ts +70 -0
- package/server/console.ts +1 -1
- package/server/di/diLifecycle.ts +16 -7
- package/server/imageOptimizer.ts +1 -1
- package/server/index.ts +1 -0
- package/server/machine/machineAccount.ts +28 -3
- package/server/machine/machineToken.ts +27 -3
- package/server/mcp/McpDispatcher.ts +2 -3
- package/server/mcp/McpExecutionContext.ts +9 -3
- package/server/migration/docPath.ts +121 -0
- package/server/migration/index.ts +15 -0
- package/server/migration/migrationChangeLog.ts +86 -0
- package/server/migration/migrationChanges.ts +70 -0
- package/server/migration/migrationCommand.ts +179 -0
- package/server/migration/migrationContext.ts +53 -0
- package/server/migration/migrationDefinition.ts +87 -0
- package/server/migration/migrationError.ts +45 -0
- package/server/migration/migrationLedger.ts +172 -0
- package/server/migration/migrationLock.ts +103 -0
- package/server/migration/migrationParams.ts +122 -0
- package/server/migration/migrationPulse.ts +39 -0
- package/server/migration/migrationRunner.ts +525 -0
- package/server/migration/migrationScope.ts +93 -0
- package/server/migration/migrationTable.ts +258 -0
- package/server/migration/migrations.ts +57 -0
- package/server/ops/opsCommand.ts +30 -1
- package/server/resolver/CascadeRunner.ts +15 -2
- package/server/resolver/signal.resolver.ts +35 -55
- package/server/routeState.ts +0 -6
- package/server/rscClient.tsx +22 -37
- package/server/rscNavigationState.ts +3 -5
- package/server/rscScrollRestoration.ts +116 -0
- package/server/rscWorker.tsx +12 -19
- package/server/sitemap.ts +4 -4
- package/server/tunnel/TunnelAgent.ts +9 -1
- package/server/webRouter.ts +17 -2
- package/service/agent.service.ts +21 -1
- package/service/endpointCacheKeys.ts +35 -0
- package/service/index.ts +1 -0
- package/service/injectInfo.ts +2 -2
- package/service/predefinedAdaptor/anthropicLlm.ts +37 -17
- package/service/predefinedAdaptor/database.adaptor.ts +15 -0
- package/service/predefinedAdaptor/documentTransfer.ts +50 -0
- package/service/predefinedAdaptor/endpointCacheControl.adaptor.ts +19 -0
- package/service/predefinedAdaptor/index.ts +1 -0
- package/service/predefinedAdaptor/llm.adaptor.ts +7 -0
- package/service/predefinedAdaptor/openaiDialect.ts +53 -17
- package/service/predefinedAdaptor/openaiLlm.ts +1 -1
- package/service/predefinedAdaptor/sql/MigrationTables.ts +90 -0
- package/service/predefinedAdaptor/sql/RawDocumentTable.ts +200 -0
- package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +6 -2
- package/service/predefinedAdaptor/sql/SqliteInsightSession.ts +4 -1
- package/service/predefinedAdaptor/sql/dialect/postgres.ts +0 -1
- package/service/predefinedAdaptor/sql/dialect/sqlite.ts +0 -1
- package/service/predefinedAdaptor/sql/types.ts +3 -1
- package/service/predefinedAdaptor/storage.adaptor.ts +19 -0
- package/signal/endpointCache.ts +23 -7
- package/signal/endpointErrorContract.ts +37 -0
- package/signal/endpointIdempotency.ts +5 -3
- package/signal/endpointInfo.ts +4 -1
- package/signal/endpointRateLimit.ts +6 -2
- package/signal/exception.ts +4 -1
- package/signal/index.ts +1 -0
- package/signal/mcp/McpDocument.ts +16 -1
- package/signal/mcp/McpUriTemplate.ts +1 -1
- package/signal/middlewareChain.ts +105 -0
- package/signal/openapi/openapi.ts +46 -2
- package/signal/serializer/fetch.serializer.ts +1 -0
- package/signal/signalContext.ts +110 -82
- package/signal/slice.ts +2 -2
- package/signal/trace.ts +8 -2
- package/signal/types.ts +13 -0
- package/store/action.ts +102 -42
- package/store/agent/ScreenImage.ts +166 -0
- package/store/agent/ScreenReader.ts +2 -1
- package/store/agent/StoreSurfaceSource.ts +41 -2
- package/store/agent/index.ts +2 -1
- package/store/databaseStateNames.ts +1 -0
- package/store/formUploadOf.ts +41 -0
- package/store/index.ts +1 -0
- package/store/liveInsert.ts +2 -2
- package/store/state.ts +9 -1
- package/store/stateBuilder.ts +8 -2
- package/store/storeInstance.ts +12 -3
- package/store/withSelector.ts +2 -2
- package/types/client/href.d.ts +11 -0
- package/types/client/index.d.ts +1 -0
- package/types/client/rscNavigation.d.ts +1 -1
- package/types/client/transitionTypes.d.ts +0 -2
- package/types/client/translator.d.ts +2 -0
- package/types/common/codeAgentWire.d.ts +7 -0
- package/types/common/index.d.ts +1 -0
- package/types/common/trimSlashes.d.ts +2 -0
- package/types/common/types.d.ts +1 -1
- package/types/constant/fieldInfo.d.ts +2 -2
- package/types/constant/fieldPlan.d.ts +33 -0
- package/types/constant/index.d.ts +3 -0
- package/types/constant/inputParser.d.ts +11 -0
- package/types/constant/invalidInputError.d.ts +8 -0
- package/types/constant/types.d.ts +1 -1
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +3 -3
- package/types/dictionary/trans.d.ts +3 -0
- package/types/document/database.d.ts +2 -3
- package/types/document/documentSchema.d.ts +1 -1
- package/types/document/noDocumentError.d.ts +6 -1
- package/types/fetch/fetchType/appliedReturn.type.d.ts +3 -1
- package/types/fetch/types.d.ts +2 -2
- package/types/index.d.ts +5 -0
- package/types/server/akanServer.d.ts +1 -1
- package/types/server/artifact/routeSeedMatcher.d.ts +8 -0
- package/types/server/di/diLifecycle.d.ts +1 -0
- package/types/server/index.d.ts +1 -0
- package/types/server/machine/machineAccount.d.ts +1 -0
- package/types/server/machine/machineToken.d.ts +9 -0
- package/types/server/migration/docPath.d.ts +20 -0
- package/types/server/migration/index.d.ts +15 -0
- package/types/server/migration/migrationChangeLog.d.ts +27 -0
- package/types/server/migration/migrationChanges.d.ts +27 -0
- package/types/server/migration/migrationCommand.d.ts +33 -0
- package/types/server/migration/migrationContext.d.ts +28 -0
- package/types/server/migration/migrationDefinition.d.ts +42 -0
- package/types/server/migration/migrationError.d.ts +15 -0
- package/types/server/migration/migrationLedger.d.ts +51 -0
- package/types/server/migration/migrationLock.d.ts +34 -0
- package/types/server/migration/migrationParams.d.ts +41 -0
- package/types/server/migration/migrationPulse.d.ts +15 -0
- package/types/server/migration/migrationRunner.d.ts +93 -0
- package/types/server/migration/migrationScope.d.ts +42 -0
- package/types/server/migration/migrationTable.d.ts +52 -0
- package/types/server/migration/migrations.d.ts +12 -0
- package/types/server/migration.d.ts +1 -0
- package/types/server/ops/opsCommand.d.ts +10 -1
- package/types/server/routeState.d.ts +1 -4
- package/types/server/rscNavigationState.d.ts +2 -3
- package/types/server/rscScrollRestoration.d.ts +14 -0
- package/types/server/sitemap.d.ts +1 -1
- package/types/service/agent.service.d.ts +2 -0
- package/types/service/endpointCacheKeys.d.ts +8 -0
- package/types/service/index.d.ts +1 -0
- package/types/service/injectInfo.d.ts +2 -2
- package/types/service/predefinedAdaptor/anthropicLlm.d.ts +9 -4
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +8 -1
- package/types/service/predefinedAdaptor/documentTransfer.d.ts +1 -0
- package/types/service/predefinedAdaptor/endpointCacheControl.adaptor.d.ts +9 -0
- package/types/service/predefinedAdaptor/index.d.ts +1 -0
- package/types/service/predefinedAdaptor/llm.adaptor.d.ts +7 -0
- package/types/service/predefinedAdaptor/openaiDialect.d.ts +20 -5
- package/types/service/predefinedAdaptor/sql/MigrationTables.d.ts +17 -0
- package/types/service/predefinedAdaptor/sql/RawDocumentTable.d.ts +38 -0
- package/types/service/predefinedAdaptor/sql/types.d.ts +1 -0
- package/types/service/predefinedAdaptor/storage.adaptor.d.ts +8 -0
- package/types/signal/endpointCache.d.ts +2 -0
- package/types/signal/endpointErrorContract.d.ts +10 -0
- package/types/signal/endpointIdempotency.d.ts +1 -0
- package/types/signal/endpointRateLimit.d.ts +1 -1
- package/types/signal/exception.d.ts +4 -1
- package/types/signal/index.d.ts +1 -0
- package/types/signal/middlewareChain.d.ts +11 -0
- package/types/signal/signalContext.d.ts +2 -6
- package/types/signal/slice.d.ts +1 -1
- package/types/signal/trace.d.ts +1 -1
- package/types/signal/types.d.ts +12 -0
- package/types/store/action.d.ts +2 -0
- package/types/store/agent/ScreenImage.d.ts +27 -0
- package/types/store/agent/StoreSurfaceSource.d.ts +1 -1
- package/types/store/agent/index.d.ts +2 -1
- package/types/store/databaseStateNames.d.ts +1 -0
- package/types/store/formUploadOf.d.ts +9 -0
- package/types/store/index.d.ts +1 -0
- package/types/store/state.d.ts +7 -1
- package/types/store/stateBuilder.d.ts +2 -1
- package/types/store/storeInstance.d.ts +2 -2
- package/types/store/withSelector.d.ts +1 -1
- package/types/ui/Agent/Composer.d.ts +2 -0
- package/types/ui/Agent/Menu.d.ts +2 -0
- package/types/ui/CsrImage.d.ts +2 -0
- package/types/ui/Data/CardList.d.ts +1 -1
- package/types/ui/Data/Dashboard.d.ts +1 -1
- package/types/ui/Data/Insight.d.ts +1 -1
- package/types/ui/Data/Item.d.ts +2 -2
- package/types/ui/Data/ListContainer.d.ts +1 -1
- package/types/ui/Data/Pagination.d.ts +1 -1
- package/types/ui/Data/TableList.d.ts +1 -1
- package/types/ui/Field/Relation.d.ts +13 -10
- package/types/ui/Field/index.d.ts +4 -4
- package/types/ui/Field/useRelationOptions.d.ts +3 -1
- package/types/ui/Field/useRelationWindow.d.ts +14 -0
- package/types/ui/Image.d.ts +3 -1
- package/types/ui/Load/View.d.ts +1 -1
- package/types/ui/Pagination.d.ts +1 -1
- package/types/ui/Signal/Arg.d.ts +1 -1
- package/types/ui/System/CSR.d.ts +1 -1
- package/types/ui/System/Client.d.ts +1 -1
- package/types/ui/System/SSR.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/core/src/plugin.d.ts +3 -3
- package/types/vendor/@akanjs/native/packages/desktop/src/page-veto.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/auth-session/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/auth-session/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/autostart/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/autostart/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/badge/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/badge/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/badge/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/biometric/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/camera/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/camera/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/camera/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/clipboard/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/clipboard/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/clipboard/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/contacts/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/device/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/device/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/device/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dialog/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dialog/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dialog/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dock/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dock/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/file-picker/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/file-picker/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/filesystem/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/filesystem/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/haptics/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/haptics/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/http/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/http/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/keep-awake/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/keep-awake/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/keep-awake/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/opener/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/opener/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/opener/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/preferences/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/preferences/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/preferences/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/secure-storage/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/secure-storage/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/share/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/share/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/single-instance/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/splash-screen/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/splash-screen/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/sqlite/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/toast/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/toast/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/window-state/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/window-state/src/index.d.ts +1 -1
- package/types/vendor/use-agentic/Compaction.d.ts +3 -1
- package/types/vendor/use-agentic/Reference.d.ts +9 -1
- package/types/vendor/use-agentic/ToolOutput.d.ts +6 -1
- package/types/vendor/use-agentic/ToolReturn.d.ts +18 -0
- package/types/vendor/use-agentic/Transcript.d.ts +7 -0
- package/types/vendor/use-agentic/index.d.ts +1 -0
- package/types/vendor/use-agentic/types.d.ts +6 -0
- package/types/webkit/useDebounce.d.ts +1 -1
- package/ui/Agent/Chat.tsx +1 -0
- package/ui/Agent/Composer.tsx +2 -0
- package/ui/Agent/Menu.tsx +5 -1
- package/ui/Agent/agentSessionOf.ts +15 -8
- package/ui/Agent/mentionDraft.ts +2 -3
- package/ui/Agent/useReferenceMenu.ts +1 -0
- package/ui/Agent/useSlashMenu.ts +1 -0
- package/ui/Clipboard.tsx +1 -0
- package/ui/CsrImage.tsx +3 -1
- package/ui/Data/CardList.tsx +1 -1
- package/ui/Data/Dashboard.tsx +1 -1
- package/ui/Data/Insight.tsx +1 -1
- package/ui/Data/Item.tsx +5 -2
- package/ui/Data/ListContainer.tsx +2 -2
- package/ui/Data/Pagination.tsx +1 -1
- package/ui/Data/TableList.tsx +1 -1
- package/ui/DraggableList.tsx +1 -0
- package/ui/Field/Collection.tsx +4 -0
- package/ui/Field/Relation.tsx +33 -14
- package/ui/Field/useRelationOptions.ts +33 -5
- package/ui/Field/useRelationWindow.ts +50 -0
- package/ui/Image.tsx +4 -4
- package/ui/Input.tsx +1 -0
- package/ui/Layout/Sider.tsx +1 -1
- package/ui/Link/Anchor.tsx +7 -5
- package/ui/Load/Units.tsx +17 -7
- package/ui/Load/View.tsx +2 -2
- package/ui/Model/Edit.tsx +1 -1
- package/ui/Model/EditModal.tsx +11 -5
- package/ui/Model/New.tsx +1 -1
- package/ui/Model/SureToRemove.tsx +1 -0
- package/ui/Model/ViewEditModal.tsx +17 -5
- package/ui/Model/ViewModal.tsx +2 -2
- package/ui/ObjectId.tsx +5 -1
- package/ui/Pagination.tsx +1 -1
- package/ui/Signal/Arg.tsx +1 -3
- package/ui/Signal/Doc.tsx +5 -1
- package/ui/System/CSR.tsx +1 -1
- package/ui/System/Client.tsx +1 -1
- package/ui/System/Reconnect.tsx +1 -1
- package/ui/System/SSR.tsx +3 -36
- package/ui/ToggleSelect.tsx +2 -0
- package/ui/styles.css +10 -103
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeBridge.swift +3 -2
- package/vendor/@akanjs/native/packages/cli/src/lib/addons.ts +4 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/apk.ts +2 -2
- package/vendor/@akanjs/native/packages/cli/src/lib/codegen.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/csp.ts +4 -3
- package/vendor/@akanjs/native/packages/cli/src/lib/googleservices.ts +7 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/hmr.ts +8 -5
- package/vendor/@akanjs/native/packages/cli/src/lib/html.ts +18 -3
- package/vendor/@akanjs/native/packages/cli/src/lib/manifestmerge.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/plist.ts +2 -4
- package/vendor/@akanjs/native/packages/cli/src/lib/toolchains.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +10 -4
- package/vendor/@akanjs/native/packages/cli/src/lib/xml.ts +4 -3
- package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +3 -3
- package/vendor/@akanjs/native/packages/core/src/kernel.ts +3 -3
- package/vendor/@akanjs/native/packages/core/src/live.ts +1 -0
- package/vendor/@akanjs/native/packages/core/src/plugin.ts +5 -3
- package/vendor/@akanjs/native/packages/desktop/src/host.ts +13 -5
- package/vendor/@akanjs/native/packages/desktop/src/lifecycle.ts +1 -0
- package/vendor/@akanjs/native/packages/desktop/src/page-veto.ts +1 -1
- package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +4 -1
- package/vendor/@akanjs/native/plugins/camera/src/web.ts +3 -3
- package/vendor/@akanjs/native/plugins/file-picker/src/index.ts +4 -1
- package/vendor/@akanjs/native/plugins/local-notifications/src/web.ts +1 -1
- package/vendor/@akanjs/native/plugins/secure-storage/src/desktop.ts +1 -1
- package/vendor/@akanjs/native/plugins/share/src/options.ts +1 -1
- package/vendor/@akanjs/native/plugins/single-instance/src/desktop.ts +3 -2
- package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +4 -1
- package/vendor/@akanjs/native/plugins/window/src/index.ts +2 -2
- package/vendor/use-agentic/Compaction.ts +38 -7
- package/vendor/use-agentic/Reference.ts +16 -9
- package/vendor/use-agentic/ToolOutput.ts +25 -3
- package/vendor/use-agentic/ToolReturn.ts +32 -0
- package/vendor/use-agentic/ToolRunner.ts +15 -13
- package/vendor/use-agentic/Transcript.ts +38 -2
- package/vendor/use-agentic/WIRE.md +20 -0
- package/vendor/use-agentic/index.ts +1 -0
- package/vendor/use-agentic/types.ts +6 -0
- package/webkit/McpAppAgent.ts +12 -2
- package/webkit/RenderLayer.tsx +23 -4
- package/webkit/useDebounce.ts +23 -14
- package/client/viewTransition.ts +0 -10
- package/types/client/viewTransition.d.ts +0 -3
package/store/action.ts
CHANGED
|
@@ -136,7 +136,7 @@ type BaseAction<
|
|
|
136
136
|
) => Promise<void>;
|
|
137
137
|
} & { [K in `set${_CapitalizedRefName}`]: (...models: (Full | Light)[]) => void } & {
|
|
138
138
|
[K in `reset${_CapitalizedRefName}`]: (model?: Full) => void;
|
|
139
|
-
};
|
|
139
|
+
} & { [K in `invalidate${_CapitalizedRefName}`]: () => void };
|
|
140
140
|
|
|
141
141
|
export type SliceAction<
|
|
142
142
|
RefName extends string,
|
|
@@ -310,9 +310,19 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
|
|
|
310
310
|
|
|
311
311
|
const names = {
|
|
312
312
|
modelForm: `${fieldName}Form`,
|
|
313
|
+
modelFormUploading: databaseStateNames(refName).modelFormUploading,
|
|
313
314
|
writeOnModel: `writeOn${className}`,
|
|
314
315
|
addModelFiles: `add${className}Files`,
|
|
315
316
|
};
|
|
317
|
+
type UploadState = { [key: string]: { [field: string]: string[] } };
|
|
318
|
+
let uploadSeq = 0;
|
|
319
|
+
const releaseUpload = (state: UploadState, field: string, ticket?: string) => {
|
|
320
|
+
const pending = state[names.modelFormUploading][field];
|
|
321
|
+
if (!pending || (ticket && !pending.includes(ticket))) return;
|
|
322
|
+
const { [field]: tickets = [], ...rest } = state[names.modelFormUploading];
|
|
323
|
+
const left = ticket ? tickets.filter((pending) => pending !== ticket) : [];
|
|
324
|
+
state[names.modelFormUploading] = left.length ? { ...rest, [field]: left } : rest;
|
|
325
|
+
};
|
|
316
326
|
const baseSetAction = {
|
|
317
327
|
[names.writeOnModel]: function (this: SetGet, path: string | (string | number)[], value: any) {
|
|
318
328
|
this.set((state: { [key: string]: any }) => {
|
|
@@ -322,9 +332,13 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
|
|
|
322
332
|
};
|
|
323
333
|
const fieldSetAction = Object.entries(modelRef[FIELD_META]).reduce((acc, [key, field]) => {
|
|
324
334
|
const namesOfField = formSetterNames(className, key);
|
|
335
|
+
const isFileField =
|
|
336
|
+
field.isClass && !!fileUploadRefName && ConstantRegistry.getRefName(field.modelRef) === fileUploadRefName;
|
|
325
337
|
const singleFieldSetAction = {
|
|
326
338
|
[namesOfField.setFieldOnModel]: function (this: SetGet, value: any | null) {
|
|
327
339
|
this.set((state: { [key: string]: any }) => {
|
|
340
|
+
|
|
341
|
+
if (isFileField && !field.isArray) releaseUpload(state as UploadState, namesOfField.field);
|
|
328
342
|
const setValue =
|
|
329
343
|
value === null
|
|
330
344
|
? null
|
|
@@ -384,56 +398,88 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
|
|
|
384
398
|
},
|
|
385
399
|
}
|
|
386
400
|
: {}),
|
|
387
|
-
...(
|
|
401
|
+
...(isFileField && fileUploadRefName
|
|
388
402
|
? {
|
|
389
403
|
[namesOfField.uploadFieldOnModel]: async function (
|
|
390
404
|
this: SetGet,
|
|
391
405
|
fileList: FileList | File[],
|
|
392
406
|
index?: number,
|
|
393
407
|
) {
|
|
394
|
-
const
|
|
408
|
+
const formId = ((this.get() as { [key: string]: any })[names.modelForm] as { id?: string }).id;
|
|
395
409
|
if (!fileList.length) return;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
410
|
+
|
|
411
|
+
const ticket = `${++uploadSeq}`;
|
|
412
|
+
this.set((state: UploadState) => {
|
|
413
|
+
const tickets = state[names.modelFormUploading][namesOfField.field] ?? [];
|
|
414
|
+
state[names.modelFormUploading] = {
|
|
415
|
+
...state[names.modelFormUploading],
|
|
416
|
+
[namesOfField.field]: field.isArray ? [...tickets, ticket] : [ticket],
|
|
417
|
+
};
|
|
418
|
+
});
|
|
419
|
+
const held = (state: { [key: string]: any }) =>
|
|
420
|
+
!!(state as UploadState)[names.modelFormUploading][namesOfField.field]?.includes(ticket) &&
|
|
421
|
+
(state[names.modelForm] as { id?: string }).id === formId;
|
|
422
|
+
let files: ProtoFile[];
|
|
423
|
+
try {
|
|
424
|
+
files = await (fetch[names.addModelFiles] as (...args: any) => Promise<ProtoFile[]>)(fileList, formId);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
this.set((state: UploadState) => releaseUpload(state, namesOfField.field, ticket));
|
|
427
|
+
throw error;
|
|
413
428
|
}
|
|
429
|
+
let isWritten = false;
|
|
430
|
+
|
|
431
|
+
this.set((state: { [key: string]: any }) => {
|
|
432
|
+
isWritten = held(state);
|
|
433
|
+
releaseUpload(state as UploadState, namesOfField.field, ticket);
|
|
434
|
+
if (!isWritten) return;
|
|
435
|
+
const form = state[names.modelForm] as { [key: string]: ProtoLightFile[] | ProtoLightFile | null };
|
|
436
|
+
if (field.isArray) {
|
|
437
|
+
const current = (form[namesOfField.field] as ProtoLightFile[] | null) ?? [];
|
|
438
|
+
const idx = Math.min(index ?? current.length, current.length);
|
|
439
|
+
form[namesOfField.field] = [...current.slice(0, idx), ...files, ...current.slice(idx)];
|
|
440
|
+
} else form[namesOfField.field] = files[0] ?? null;
|
|
441
|
+
});
|
|
442
|
+
if (!isWritten) return;
|
|
443
|
+
const postSet = (this as unknown as { [key: string]: ((value: unknown) => unknown) | undefined })[
|
|
444
|
+
namesOfField.postSetField
|
|
445
|
+
];
|
|
446
|
+
if (postSet)
|
|
447
|
+
void postSet.call(this, (this.get() as { [key: string]: any })[names.modelForm][namesOfField.field]);
|
|
414
448
|
|
|
415
449
|
const fileReadName = ConstantRegistry.isLight(field.modelRef)
|
|
416
450
|
? `light${capitalize(fileUploadRefName)}`
|
|
417
451
|
: fileUploadRefName;
|
|
452
|
+
const holdsFile = (id: string) => {
|
|
453
|
+
const value = (this.get() as { [key: string]: { [key: string]: unknown } })[names.modelForm][
|
|
454
|
+
namesOfField.field
|
|
455
|
+
] as ProtoLightFile | ProtoLightFile[] | null;
|
|
456
|
+
return Array.isArray(value) ? value.some((heldFile) => heldFile.id === id) : value?.id === id;
|
|
457
|
+
};
|
|
418
458
|
files.forEach((file) => {
|
|
419
459
|
let attemptsLeft = UPLOAD_POLL_ATTEMPTS;
|
|
420
460
|
const intervalKey = setInterval(() => {
|
|
421
461
|
void (async () => {
|
|
462
|
+
if (!holdsFile(file.id)) {
|
|
463
|
+
clearInterval(intervalKey);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
422
466
|
attemptsLeft -= 1;
|
|
423
467
|
try {
|
|
424
468
|
const currentFile = await (
|
|
425
469
|
(fetch as { [key: string]: any })[fileReadName] as (id: string) => Promise<ProtoLightFile>
|
|
426
470
|
)(file.id);
|
|
427
|
-
if (
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
471
|
+
if (!holdsFile(currentFile.id)) {
|
|
472
|
+
clearInterval(intervalKey);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
this.set(
|
|
476
|
+
(state: { [key: string]: { [key: string]: ProtoLightFile[] | ProtoLightFile | null } }) => {
|
|
477
|
+
const value = state[names.modelForm][namesOfField.field];
|
|
478
|
+
state[names.modelForm][namesOfField.field] = Array.isArray(value)
|
|
479
|
+
? value.map((heldFile) => (heldFile.id === currentFile.id ? currentFile : heldFile))
|
|
480
|
+
: currentFile;
|
|
481
|
+
},
|
|
482
|
+
);
|
|
437
483
|
if (currentFile.status !== "uploading" || attemptsLeft <= 0) clearInterval(intervalKey);
|
|
438
484
|
} catch (error) {
|
|
439
485
|
clearInterval(intervalKey);
|
|
@@ -497,10 +543,14 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
497
543
|
viewModel: `view${className}`,
|
|
498
544
|
setModel: `set${className}`,
|
|
499
545
|
resetModel: `reset${className}`,
|
|
546
|
+
invalidateModel: `invalidate${className}`,
|
|
500
547
|
loadModelDraft: `load${className}FormDraft`,
|
|
501
548
|
restoreModelDraft: `restore${className}FormDraft`,
|
|
502
549
|
discardModelDraft: `discard${className}FormDraft`,
|
|
503
550
|
};
|
|
551
|
+
|
|
552
|
+
const isUploadingIn = (state: { [key: string]: any }) =>
|
|
553
|
+
Object.keys(state[names.modelFormUploading] as object).length > 0;
|
|
504
554
|
const defaultKeyOf = (sliceName: string) => capitalize(sliceName).replace(names.Model, names.defaultModel);
|
|
505
555
|
|
|
506
556
|
const staleAtOfSlices = () => {
|
|
@@ -602,7 +652,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
602
652
|
const defaultModel = currentState[defaultKeyOf(sliceName)] as Full;
|
|
603
653
|
const modelInput = (cnst.input.purify as (form: any) => DefaultOf<Input> | null)(modelForm);
|
|
604
654
|
|
|
605
|
-
if (!modelInput) return;
|
|
655
|
+
if (!modelInput || isUploadingIn(currentState)) return;
|
|
606
656
|
this.set({ [names.modelLoading]: true });
|
|
607
657
|
const model = await servedFetch<Full>(names.createModel)(modelInput, { onError });
|
|
608
658
|
this.set({
|
|
@@ -627,7 +677,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
627
677
|
const modelForm = currentState[names.modelForm] as Input & { id: string };
|
|
628
678
|
const defaultModel = currentState[defaultKeyOf(sliceName)] as Full;
|
|
629
679
|
const modelInput = (cnst.input.purify as (form: any) => DefaultOf<Input> | null)(modelForm);
|
|
630
|
-
if (!modelInput) return;
|
|
680
|
+
if (!modelInput || isUploadingIn(currentState)) return;
|
|
631
681
|
if (model?.id === modelForm.id) this.set({ [names.modelLoading]: modelForm.id });
|
|
632
682
|
const updatedModel = await servedFetch<Full>(names.updateModel)(modelForm.id, modelInput, { onError });
|
|
633
683
|
this.set({
|
|
@@ -730,7 +780,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
730
780
|
const modelForm = currentState[names.modelForm] as Input;
|
|
731
781
|
const modelSubmit = currentState[names.modelSubmit] as { disabled: boolean };
|
|
732
782
|
const modelInput = (cnst.input.purify as (obj: any) => DefaultOf<Input> | null)(modelForm);
|
|
733
|
-
|
|
783
|
+
const isDisabled = !modelInput || isUploadingIn(currentState) || disabled;
|
|
784
|
+
this.set({ [names.modelSubmit]: { ...modelSubmit, disabled: isDisabled } });
|
|
734
785
|
},
|
|
735
786
|
[names.submitModel]: async function (this: SetGet, option?: CreateOption<Full>) {
|
|
736
787
|
const currentState = this.get() as { [key: string]: any };
|
|
@@ -855,6 +906,9 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
855
906
|
});
|
|
856
907
|
return model ?? null;
|
|
857
908
|
},
|
|
909
|
+
[names.invalidateModel]: function (this: SetGet) {
|
|
910
|
+
this.set(staleAtOfSlices());
|
|
911
|
+
},
|
|
858
912
|
|
|
859
913
|
[names.loadModelDraft]: async function (this: SetGet, draftScope?: string) {
|
|
860
914
|
const currentState = this.get() as { [key: string]: any };
|
|
@@ -897,6 +951,10 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
897
951
|
const hasMoreFrom = (batch: unknown[], askedFor: number) => ({
|
|
898
952
|
[namesOfSlice.hasMoreOfModel]: batch.length >= askedFor && askedFor > 0,
|
|
899
953
|
});
|
|
954
|
+
|
|
955
|
+
const lastPageFrom = (count: number, limit: number) => ({
|
|
956
|
+
[namesOfSlice.lastPageOfModel]: limit > 0 ? Math.max(Math.floor((count - 1) / limit) + 1, 1) : 1,
|
|
957
|
+
});
|
|
900
958
|
const fetchList = (queryArgs: unknown[], skip: number, limit: number, sort: Sort, options?: object) =>
|
|
901
959
|
(fetch[namesOfSlice.modelList] as (...args: any[]) => Promise<Light[]>)(
|
|
902
960
|
...expandQueryArgs(queryArgs, slice.args),
|
|
@@ -979,6 +1037,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
979
1037
|
isQueryEqual(sort as unknown as object, sortOfModel as unknown as object)
|
|
980
1038
|
)
|
|
981
1039
|
return;
|
|
1040
|
+
|
|
1041
|
+
const requestedAt = new Date();
|
|
982
1042
|
await loadList(
|
|
983
1043
|
this,
|
|
984
1044
|
() =>
|
|
@@ -995,8 +1055,8 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
995
1055
|
[namesOfSlice.modelList]: modelList,
|
|
996
1056
|
[namesOfSlice.modelInsight]: modelInsight,
|
|
997
1057
|
[namesOfSlice.modelInitList]: modelList,
|
|
998
|
-
[namesOfSlice.modelInitAt]:
|
|
999
|
-
|
|
1058
|
+
[namesOfSlice.modelInitAt]: requestedAt,
|
|
1059
|
+
...lastPageFrom(modelInsight.count, limit),
|
|
1000
1060
|
...hasMoreFrom(modelDataList, fetchLimit),
|
|
1001
1061
|
[namesOfSlice.limitOfModel]: limit,
|
|
1002
1062
|
[namesOfSlice.queryArgsOfModel]: queryArgs,
|
|
@@ -1085,7 +1145,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1085
1145
|
() => fetchList(queryArgsOfModel, (page - 1) * limit, limit, sortOfModel, options),
|
|
1086
1146
|
(modelDataList) => ({
|
|
1087
1147
|
[namesOfSlice.modelList]: new DataList(modelDataList),
|
|
1088
|
-
|
|
1148
|
+
...lastPageFrom(modelInsight.count, limit),
|
|
1089
1149
|
[namesOfSlice.limitOfModel]: limit,
|
|
1090
1150
|
[namesOfSlice.pageOfModel]: page,
|
|
1091
1151
|
[namesOfSlice.isCumulativeOfModel]: false,
|
|
@@ -1126,7 +1186,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1126
1186
|
[namesOfSlice.queryArgsOfModel]: queryArgs,
|
|
1127
1187
|
[namesOfSlice.modelList]: new DataList(modelDataList),
|
|
1128
1188
|
[namesOfSlice.modelInsight]: modelInsight,
|
|
1129
|
-
|
|
1189
|
+
...lastPageFrom(modelInsight.count, limitOfModel),
|
|
1130
1190
|
[namesOfSlice.pageOfModel]: 1,
|
|
1131
1191
|
[namesOfSlice.isCumulativeOfModel]: false,
|
|
1132
1192
|
...hasMoreFrom(modelDataList, limitOfModel),
|
|
@@ -1155,19 +1215,19 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1155
1215
|
|
|
1156
1216
|
[namesOfSlice.applyLiveModel]: function (this: SetGet, event: LiveEventPayload) {
|
|
1157
1217
|
const currentState = this.get() as { [key: string]: any };
|
|
1158
|
-
if (currentState[namesOfSlice.modelListLoading] as boolean) return;
|
|
1159
1218
|
const modelList = currentState[namesOfSlice.modelList] as DataList<Light>;
|
|
1160
1219
|
const staleAt = { [namesOfSlice.modelStaleAt]: new Date() };
|
|
1161
|
-
|
|
1220
|
+
|
|
1221
|
+
if (event.op === "invalidate" || (currentState[namesOfSlice.modelListLoading] as boolean)) {
|
|
1162
1222
|
this.set(staleAt);
|
|
1163
1223
|
return;
|
|
1164
1224
|
}
|
|
1165
1225
|
const modelInsight = currentState[namesOfSlice.modelInsight] as Insight & BaseInsight;
|
|
1166
|
-
const limit =
|
|
1226
|
+
const limit = currentState[namesOfSlice.limitOfModel] as number;
|
|
1167
1227
|
const isCumulative = currentState[namesOfSlice.isCumulativeOfModel] as boolean;
|
|
1168
1228
|
const countedTo = (count: number) => ({
|
|
1169
1229
|
[namesOfSlice.modelInsight]: new cnst.insight().set({ ...modelInsight, count }),
|
|
1170
|
-
|
|
1230
|
+
...lastPageFrom(count, limit),
|
|
1171
1231
|
});
|
|
1172
1232
|
if (event.op === "leave") {
|
|
1173
1233
|
if (!modelList.has(event.id)) return;
|
|
@@ -1206,7 +1266,7 @@ export const makeActions = (refName: string, slice: { [key: string]: SerializedS
|
|
|
1206
1266
|
}
|
|
1207
1267
|
|
|
1208
1268
|
const inserted = [...modelList.slice(0, placement), light, ...modelList.slice(placement)];
|
|
1209
|
-
const placed = isCumulative ? inserted : inserted.slice(0, limit);
|
|
1269
|
+
const placed = isCumulative || !limit ? inserted : inserted.slice(0, limit);
|
|
1210
1270
|
this.set({
|
|
1211
1271
|
[namesOfSlice.modelList]: new DataList(placed as Light[]),
|
|
1212
1272
|
...countedTo(modelInsight.count + 1),
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Binary } from "akanjs/base";
|
|
2
|
+
import { type MessageAttachment, ToolOutput } from "../../vendor/use-agentic";
|
|
3
|
+
import { ScreenTarget } from "./ScreenTarget";
|
|
4
|
+
|
|
5
|
+
export interface ScreenImageView {
|
|
6
|
+
attachment: MessageAttachment;
|
|
7
|
+
/** The picture's size and how it was sent, for a model asked about its proportions or resolution. */
|
|
8
|
+
size: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A picture on the screen, handed to the model as the browser drew it: redrawn into a JPEG no longer than
|
|
13
|
+
* `maxEdge`, inlined as bytes. Redrawing turns HEIC, AVIF and SVG into a type every provider reads, bounds what one
|
|
14
|
+
* picture costs, and leaves the provider nothing to download — a storage path or a `blob:` address it cannot reach,
|
|
15
|
+
* or a signed URL that expires while the conversation still carries it.
|
|
16
|
+
*/
|
|
17
|
+
export class ScreenImage {
|
|
18
|
+
/** The long edge Anthropic downscales past; a bigger picture spends upload and latency on pixels nobody reads. */
|
|
19
|
+
static readonly maxEdge = 1568;
|
|
20
|
+
static readonly quality = 0.85;
|
|
21
|
+
static readonly loadMs = 10_000;
|
|
22
|
+
|
|
23
|
+
static readonly #typeByExtension = new Map([
|
|
24
|
+
["jpg", "image/jpeg"],
|
|
25
|
+
["jpeg", "image/jpeg"],
|
|
26
|
+
["png", "image/png"],
|
|
27
|
+
["gif", "image/gif"],
|
|
28
|
+
["webp", "image/webp"],
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
/** The `<img>` showing `src` in `root`, matched on what readScreen prints, its absolute form, or the attribute. */
|
|
32
|
+
static find(src: string, root?: HTMLElement): HTMLImageElement | null {
|
|
33
|
+
const scope = root ?? (typeof document === "undefined" ? null : document.body);
|
|
34
|
+
if (!scope) return null;
|
|
35
|
+
for (const image of scope.querySelectorAll("img"))
|
|
36
|
+
if (
|
|
37
|
+
(image.currentSrc === src || image.src === src || image.getAttribute("src") === src) &&
|
|
38
|
+
ScreenTarget.visible(image)
|
|
39
|
+
)
|
|
40
|
+
return image;
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static async view(image: HTMLImageElement): Promise<ScreenImageView> {
|
|
45
|
+
const src = image.currentSrc || image.src;
|
|
46
|
+
const name = ScreenImage.#nameOf(image, src);
|
|
47
|
+
|
|
48
|
+
const loaded = image.complete && image.naturalWidth > 0 ? image : await ScreenImage.#load(src, false);
|
|
49
|
+
const drawn =
|
|
50
|
+
(loaded ? await ScreenImage.#draw(loaded, name) : null) ??
|
|
51
|
+
(await ScreenImage.#drawFrom(await ScreenImage.#load(src, true), name));
|
|
52
|
+
if (drawn) return drawn;
|
|
53
|
+
const mimeType = ScreenImage.#typeOf(src);
|
|
54
|
+
if (mimeType && ScreenImage.publiclyFetchable(src))
|
|
55
|
+
return {
|
|
56
|
+
attachment: { name, mimeType, url: src },
|
|
57
|
+
size: `${ScreenImage.#sizeOf(image)}, sent by address because this page may not read its pixels`,
|
|
58
|
+
};
|
|
59
|
+
throw new Error(
|
|
60
|
+
`${name} cannot be viewed: its host does not let this page read the picture, and the model's provider cannot download it from ${src}. Tell the user you cannot see it.`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether a provider could download `url` itself — an http(s) address off loopback, private, link-local and CGNAT
|
|
66
|
+
* ranges. It decides between an address and no picture at all; it is not an SSRF guard and must not become one.
|
|
67
|
+
*/
|
|
68
|
+
static publiclyFetchable(url: string): boolean {
|
|
69
|
+
if (!/^https?:\/\//i.test(url)) return false;
|
|
70
|
+
let hostname: string;
|
|
71
|
+
try {
|
|
72
|
+
hostname = new URL(url).hostname.toLowerCase();
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname.endsWith(".local")) return false;
|
|
77
|
+
if (hostname === "internal" || hostname.endsWith(".internal")) return false;
|
|
78
|
+
if (hostname.startsWith("[")) return !ScreenImage.#privateV6(hostname.slice(1, -1));
|
|
79
|
+
return !/^(?:0\.|10\.|127\.|169\.254\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.|100\.(?:6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.)/.test(
|
|
80
|
+
hostname,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static #privateV6(address: string): boolean {
|
|
85
|
+
if (address === "::" || address === "::1" || address.startsWith("::ffff:")) return true;
|
|
86
|
+
const first = Number.parseInt(address.split(":")[0] || "0", 16);
|
|
87
|
+
return (first & 0xfe00) === 0xfc00 || (first & 0xffc0) === 0xfe80;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static async #drawFrom(image: HTMLImageElement | null, name: string): Promise<ScreenImageView | null> {
|
|
91
|
+
return image ? await ScreenImage.#draw(image, name) : null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static async #draw(image: HTMLImageElement, name: string): Promise<ScreenImageView | null> {
|
|
95
|
+
const width = image.naturalWidth || image.width;
|
|
96
|
+
const height = image.naturalHeight || image.height;
|
|
97
|
+
if (!width || !height) return null;
|
|
98
|
+
const scale = Math.min(1, ScreenImage.maxEdge / Math.max(width, height));
|
|
99
|
+
const canvas = document.createElement("canvas");
|
|
100
|
+
canvas.width = Math.max(1, Math.round(width * scale));
|
|
101
|
+
canvas.height = Math.max(1, Math.round(height * scale));
|
|
102
|
+
const context = canvas.getContext("2d");
|
|
103
|
+
if (!context) return null;
|
|
104
|
+
|
|
105
|
+
context.fillStyle = "#ffffff";
|
|
106
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
107
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
108
|
+
let blob: Blob | null;
|
|
109
|
+
try {
|
|
110
|
+
blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, "image/jpeg", ScreenImage.quality));
|
|
111
|
+
} catch {
|
|
112
|
+
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
if (!blob) return null;
|
|
116
|
+
const bytes = new Uint8Array(await blob.arrayBuffer());
|
|
117
|
+
if (bytes.length > ToolOutput.attachmentBytes)
|
|
118
|
+
throw new Error(`${name} is ${bytes.length} bytes even as a JPEG, past what one tool result may carry.`);
|
|
119
|
+
return {
|
|
120
|
+
attachment: { name, mimeType: "image/jpeg", data: Binary.toBase64(bytes) },
|
|
121
|
+
size: `${width}×${height}, sent as ${canvas.width}×${canvas.height} JPEG`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static #load(src: string, cors: boolean): Promise<HTMLImageElement | null> {
|
|
126
|
+
return new Promise((resolve) => {
|
|
127
|
+
const image = new Image();
|
|
128
|
+
if (cors) image.crossOrigin = "anonymous";
|
|
129
|
+
const timer = setTimeout(() => resolve(null), ScreenImage.loadMs);
|
|
130
|
+
image.onload = () => {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
resolve(image);
|
|
133
|
+
};
|
|
134
|
+
image.onerror = () => {
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
resolve(null);
|
|
137
|
+
};
|
|
138
|
+
image.src = src;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static #nameOf(image: HTMLImageElement, src: string): string {
|
|
143
|
+
const alt = image.getAttribute("alt")?.trim();
|
|
144
|
+
if (alt) return alt;
|
|
145
|
+
if (!/^https?:/i.test(src)) return "image";
|
|
146
|
+
try {
|
|
147
|
+
return decodeURIComponent(new URL(src).pathname.split("/").pop() ?? "") || "image";
|
|
148
|
+
} catch {
|
|
149
|
+
return "image";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static #typeOf(src: string): string | null {
|
|
154
|
+
let pathname: string;
|
|
155
|
+
try {
|
|
156
|
+
pathname = new URL(src).pathname;
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return ScreenImage.#typeByExtension.get(pathname.split(".").pop()?.toLowerCase() ?? "") ?? null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static #sizeOf(image: HTMLImageElement): string {
|
|
164
|
+
return image.naturalWidth ? `${image.naturalWidth}×${image.naturalHeight}` : "size unknown";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -13,6 +13,7 @@ const skipTags = new Set([
|
|
|
13
13
|
]);
|
|
14
14
|
const headingLevels = { H1: 1, H2: 2, H3: 3, H4: 4, H5: 5, H6: 6 } as const;
|
|
15
15
|
const headingSelector = "h1, h2, h3, h4, h5, h6";
|
|
16
|
+
const unprintedHrefScheme = /^(?:javascript|data|vbscript):/i;
|
|
16
17
|
const blockTags = new Set([
|
|
17
18
|
"ADDRESS",
|
|
18
19
|
"ARTICLE",
|
|
@@ -255,7 +256,7 @@ export class ScreenReader {
|
|
|
255
256
|
const before = this.#buffer;
|
|
256
257
|
this.#walkChildren(el);
|
|
257
258
|
const text = this.#buffer.slice(before.length).replace(/\s+/g, " ").trim();
|
|
258
|
-
if (href && href !== "#" && !
|
|
259
|
+
if (href && href !== "#" && !unprintedHrefScheme.test(href) && text !== href) this.#buffer += ` (${href})`;
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
static #off(el: HTMLElement) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { router } from "akanjs/client";
|
|
2
|
-
import { AgenticSurface, type SurfaceSource, type ToolEntry } from "../../vendor/use-agentic";
|
|
2
|
+
import { AgenticSurface, type SurfaceSource, type ToolEntry, ToolReturn } from "../../vendor/use-agentic";
|
|
3
3
|
import { AgentBridge } from "./AgentBridge";
|
|
4
4
|
import { ScreenFlash } from "./ScreenFlash";
|
|
5
|
+
import { ScreenImage } from "./ScreenImage";
|
|
5
6
|
import { ScreenReader } from "./ScreenReader";
|
|
6
7
|
import { ScreenSettle } from "./ScreenSettle";
|
|
7
8
|
import { ScreenTarget } from "./ScreenTarget";
|
|
@@ -9,7 +10,7 @@ import { ScreenTarget } from "./ScreenTarget";
|
|
|
9
10
|
/** Every screen's built-in tools. A same-named hook tool shadows one at root scope only; a zone uses `builtins`. */
|
|
10
11
|
export class StoreSurfaceSource implements SurfaceSource {
|
|
11
12
|
/** What a session's `builtins` option selects from, in build order. */
|
|
12
|
-
static readonly builtins = ["navigate", "goBack", "readScreen", "readState", "highlight"] as const;
|
|
13
|
+
static readonly builtins = ["navigate", "goBack", "readScreen", "readState", "highlight", "viewImage"] as const;
|
|
13
14
|
|
|
14
15
|
#bridge: AgentBridge | null;
|
|
15
16
|
readonly #builtins = new Map<string, ToolEntry[]>();
|
|
@@ -29,6 +30,7 @@ export class StoreSurfaceSource implements SurfaceSource {
|
|
|
29
30
|
StoreSurfaceSource.#readScreen(viewKey),
|
|
30
31
|
this.#readState(viewKey),
|
|
31
32
|
StoreSurfaceSource.#highlight(viewKey),
|
|
33
|
+
StoreSurfaceSource.#viewImage(viewKey),
|
|
32
34
|
];
|
|
33
35
|
this.#builtins.set(viewKey, builtins);
|
|
34
36
|
}
|
|
@@ -158,6 +160,43 @@ export class StoreSurfaceSource implements SurfaceSource {
|
|
|
158
160
|
};
|
|
159
161
|
}
|
|
160
162
|
|
|
163
|
+
static #viewImage(viewKey: string): ToolEntry {
|
|
164
|
+
const absent = (src: string) =>
|
|
165
|
+
`No picture at ${src} is on screen now. Call readScreen with images: true and pass the <address> of an [image: <alt>](<address>) it prints; a picture that has left the screen cannot be viewed.`;
|
|
166
|
+
return {
|
|
167
|
+
name: "viewImage",
|
|
168
|
+
description:
|
|
169
|
+
"Look at one picture on screen, to answer from what it shows rather than from its caption. readScreen with `images: true` prints each picture as `[image: <alt>](<address>)`, or `[image](<address>)` when it has no alt; pass that address. Each picture costs this turn over a thousand tokens, so view only the ones the answer needs.",
|
|
170
|
+
parameters: {
|
|
171
|
+
type: "object",
|
|
172
|
+
properties: {
|
|
173
|
+
src: {
|
|
174
|
+
type: "string",
|
|
175
|
+
description: "The <address> of an [image: <alt>](<address>) readScreen printed, without the parentheses.",
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
required: ["src"],
|
|
179
|
+
additionalProperties: false,
|
|
180
|
+
},
|
|
181
|
+
settle: false,
|
|
182
|
+
|
|
183
|
+
guard: (args) => {
|
|
184
|
+
const src = typeof args.src === "string" ? args.src.trim() : "";
|
|
185
|
+
if (!src) return "viewImage needs a src.";
|
|
186
|
+
if (src.startsWith("data:"))
|
|
187
|
+
return "A data: address is the picture's bytes, not an address readScreen prints. Pass one readScreen printed.";
|
|
188
|
+
return ScreenImage.find(src, StoreSurfaceSource.#zoneRoot(viewKey)) ? true : absent(src);
|
|
189
|
+
},
|
|
190
|
+
run: async (args) => {
|
|
191
|
+
const src = String(args.src).trim();
|
|
192
|
+
const image = ScreenImage.find(src, StoreSurfaceSource.#zoneRoot(viewKey));
|
|
193
|
+
if (!image) throw new Error(absent(src));
|
|
194
|
+
const { attachment, size } = await ScreenImage.view(image);
|
|
195
|
+
return ToolReturn.attach(`Attached ${attachment.name}: ${size}.`, [attachment]);
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
161
200
|
#readState(viewKey: string): ToolEntry {
|
|
162
201
|
return {
|
|
163
202
|
name: "readState",
|
package/store/agent/index.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
|
|
2
|
-
export { AgentAbort, AgentProgress, type AgentProgressStep } from "../../vendor/use-agentic";
|
|
2
|
+
export { AgentAbort, AgentProgress, type AgentProgressStep, ToolReturn } from "../../vendor/use-agentic";
|
|
3
3
|
export * from "./AgentBridge";
|
|
4
4
|
export * from "./AgentContext";
|
|
5
5
|
export * from "./AgentCursor";
|
|
6
6
|
export * from "./AgentPrompts";
|
|
7
7
|
export * from "./AgentVisual";
|
|
8
8
|
export * from "./ScreenFlash";
|
|
9
|
+
export * from "./ScreenImage";
|
|
9
10
|
export * from "./ScreenReader";
|
|
10
11
|
export * from "./ScreenSettle";
|
|
11
12
|
export * from "./ScreenTarget";
|
|
@@ -7,6 +7,7 @@ export const databaseStateNames = (refName: string) => {
|
|
|
7
7
|
modelLoading: `${refName}Loading`,
|
|
8
8
|
modelForm: `${refName}Form`,
|
|
9
9
|
modelFormLoading: `${refName}FormLoading`,
|
|
10
|
+
modelFormUploading: `${refName}FormUploading`,
|
|
10
11
|
modelSubmit: `${refName}Submit`,
|
|
11
12
|
modelViewAt: `${refName}ViewAt`,
|
|
12
13
|
modelModal: `${refName}Modal`,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { capitalize } from "akanjs/common";
|
|
2
|
+
import { actionTagOf } from "./actionTag";
|
|
3
|
+
import { StoreRegistry } from "./storeRegistry";
|
|
4
|
+
|
|
5
|
+
interface HeldFile {
|
|
6
|
+
id: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface FormUpload {
|
|
10
|
+
upload: (fileList: FileList | File[]) => Promise<void>;
|
|
11
|
+
refresh: (files: HeldFile[]) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const formUploadOf = (setter: unknown): FormUpload | null => {
|
|
15
|
+
const tag = actionTagOf(setter);
|
|
16
|
+
const [formKey, field, ...rest] = tag?.state?.split(".") ?? [];
|
|
17
|
+
if (!formKey?.endsWith("Form") || !field || rest.length) return null;
|
|
18
|
+
const [Model, Field] = [capitalize(formKey.slice(0, -"Form".length)), capitalize(field)];
|
|
19
|
+
if (tag?.action !== `set${Field}On${Model}`) return null;
|
|
20
|
+
const actions = StoreRegistry.instance.do as unknown as { [key: string]: unknown };
|
|
21
|
+
const upload = actions[`upload${Field}On${Model}`];
|
|
22
|
+
const writeOn = actions[`writeOn${Model}`] as ((path: string, value: unknown) => unknown) | undefined;
|
|
23
|
+
if (typeof upload !== "function" || typeof writeOn !== "function") return null;
|
|
24
|
+
|
|
25
|
+
const refresh = (files: HeldFile[]) => {
|
|
26
|
+
const form = StoreRegistry.instance.get()[formKey] as { [key: string]: unknown } | undefined;
|
|
27
|
+
const held = form?.[field] as HeldFile | HeldFile[] | null | undefined;
|
|
28
|
+
const fresh = (file: HeldFile) => files.find((updated) => updated.id === file.id);
|
|
29
|
+
if (Array.isArray(held)) {
|
|
30
|
+
if (held.some((file) => fresh(file)))
|
|
31
|
+
void writeOn(
|
|
32
|
+
field,
|
|
33
|
+
held.map((file) => fresh(file) ?? file),
|
|
34
|
+
);
|
|
35
|
+
} else if (held) {
|
|
36
|
+
const updated = fresh(held);
|
|
37
|
+
if (updated) void writeOn(field, updated);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return { upload: upload as FormUpload["upload"], refresh };
|
|
41
|
+
};
|
package/store/index.ts
CHANGED
package/store/liveInsert.ts
CHANGED
|
@@ -39,9 +39,9 @@ export const livePlacementIndex = ({
|
|
|
39
39
|
});
|
|
40
40
|
if (!orderable) return null;
|
|
41
41
|
if (index !== -1) return index;
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
if (cumulative) return hasMore ? null : list.length;
|
|
44
|
-
return list.length < limit ? list.length : null;
|
|
44
|
+
return !limit || list.length < limit ? list.length : null;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
const compareRows = (left: LiveSortableRow, right: LiveSortableRow, paths: [string, 1 | -1][]): number | null => {
|