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
|
@@ -176,7 +176,7 @@ export function androidManifest(
|
|
|
176
176
|
<uses-sdk android:minSdkVersion="${MIN_SDK}" android:targetSdkVersion="${TARGET_SDK}" />
|
|
177
177
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
178
178
|
${permissions.map((p) => ` <uses-permission android:name="${xml(p)}" />`).join("\n")}
|
|
179
|
-
${manifestXml.map((x) => ` ${x.replaceAll(
|
|
179
|
+
${manifestXml.map((x) => ` ${x.replaceAll(`\${applicationId}`, appId)}`).join("\n")}
|
|
180
180
|
|
|
181
181
|
<application
|
|
182
182
|
android:name="com.akanjs.runtime.AkanNativeApplication"
|
|
@@ -221,7 +221,7 @@ ${config.deepLinks.domains
|
|
|
221
221
|
${pathPrefixes.map((prefix) => ` <data android:pathPrefix="${xml(prefix)}" />\n`).join("")} </intent-filter>`,
|
|
222
222
|
)
|
|
223
223
|
.join("\n")}
|
|
224
|
-
${activityXml.map((x) => ` ${x.replaceAll(
|
|
224
|
+
${activityXml.map((x) => ` ${x.replaceAll(`\${applicationId}`, appId)}`).join("\n")}
|
|
225
225
|
</activity>
|
|
226
226
|
|
|
227
227
|
<!-- Framework-only replacement for androidx FileProvider (camera EXTRA_OUTPUT, plugins.md C4) -->
|
|
@@ -230,7 +230,7 @@ ${activityXml.map((x) => ` ${x.replaceAll("${applicationId}", appId)}
|
|
|
230
230
|
android:authorities="${appId}.akan-native.files"
|
|
231
231
|
android:exported="false"
|
|
232
232
|
android:grantUriPermissions="true" />
|
|
233
|
-
${applicationXml.map((x) => ` ${x.replaceAll(
|
|
233
|
+
${applicationXml.map((x) => ` ${x.replaceAll(`\${applicationId}`, appId)}`).join("\n")}
|
|
234
234
|
</application>
|
|
235
235
|
</manifest>
|
|
236
236
|
`;
|
|
@@ -453,9 +453,9 @@ export function updateReportOutcome(status: number): UpdateReportOutcome {
|
|
|
453
453
|
* §10.2.3, digits only), else null. An HTTP date is not read; the batch goes at the next launch or return instead.
|
|
454
454
|
*/
|
|
455
455
|
export function updateReportRetryAt(retryAfter: string | null, now: number): number | null {
|
|
456
|
-
const
|
|
457
|
-
if (
|
|
458
|
-
const seconds = Number(
|
|
456
|
+
const digits = /^[ \t]*([0-9]+)[ \t]*$/.exec(retryAfter ?? "")?.[1];
|
|
457
|
+
if (digits === undefined) return null;
|
|
458
|
+
const seconds = Number(digits);
|
|
459
459
|
return Number.isFinite(seconds) ? now + seconds : null;
|
|
460
460
|
}
|
|
461
461
|
|
|
@@ -82,16 +82,18 @@ type WithOptions<F> = F extends (...args: infer P) => infer R
|
|
|
82
82
|
*/
|
|
83
83
|
export type PluginMethods<Api> = { [K in keyof Api]: Api[K] & WithOptions<Api[K]> };
|
|
84
84
|
|
|
85
|
-
export type Plugin<Api, Events =
|
|
85
|
+
export type Plugin<Api, Events = Record<never, never>> = PluginMethods<Api> & PluginHandle<Api, Events>;
|
|
86
86
|
|
|
87
87
|
const RESERVED = new Set([...RESERVED_MEMBERS, "$$typeof"]);
|
|
88
88
|
|
|
89
89
|
/** Identity helper that gives web implementations their types. */
|
|
90
|
-
export function defineWebPlugin<Api, Events =
|
|
90
|
+
export function defineWebPlugin<Api, Events = Record<never, never>>(
|
|
91
|
+
impl: WebPlugin<Api, Events>,
|
|
92
|
+
): WebPlugin<Api, Events> {
|
|
91
93
|
return impl;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
export function definePlugin<Api extends object, Events extends object =
|
|
96
|
+
export function definePlugin<Api extends object, Events extends object = Record<never, never>>(
|
|
95
97
|
id: string,
|
|
96
98
|
options: PluginOptions<Api, Events>,
|
|
97
99
|
): Plugin<Api, Events> {
|
|
@@ -244,7 +244,14 @@ export function startHost(plugins: DesktopPlugin[]): void {
|
|
|
244
244
|
|
|
245
245
|
if (req.method === "hang" && boot.dev)
|
|
246
246
|
return new Promise((resolve) =>
|
|
247
|
-
signal.addEventListener(
|
|
247
|
+
signal.addEventListener(
|
|
248
|
+
"abort",
|
|
249
|
+
() => {
|
|
250
|
+
cancels++;
|
|
251
|
+
resolve(undefined);
|
|
252
|
+
},
|
|
253
|
+
{ once: true },
|
|
254
|
+
),
|
|
248
255
|
);
|
|
249
256
|
|
|
250
257
|
if (req.method === "info" && boot.dev) {
|
|
@@ -379,10 +386,11 @@ export function startHost(plugins: DesktopPlugin[]): void {
|
|
|
379
386
|
const started = performance.now();
|
|
380
387
|
for (let frames = 0; ; frames++) {
|
|
381
388
|
if (frames >= DRAIN_FRAMES || performance.now() - started > DRAIN_MS) {
|
|
382
|
-
continuation
|
|
383
|
-
continuation =
|
|
384
|
-
|
|
385
|
-
|
|
389
|
+
if (continuation === null)
|
|
390
|
+
continuation = setTimeout(() => {
|
|
391
|
+
continuation = null;
|
|
392
|
+
drain();
|
|
393
|
+
}, 0);
|
|
386
394
|
return;
|
|
387
395
|
}
|
|
388
396
|
const size = native.akan_native_poll(buffer, buffer.length);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
export type QuitReason = "user" | "session" | "lastWindowClosed";
|
|
4
4
|
|
|
5
5
|
/** Return false (or resolve to false) to keep the app running / the window open. */
|
|
6
|
+
|
|
6
7
|
export type Veto<E> = (event: E) => boolean | void | Promise<boolean | void>;
|
|
7
8
|
|
|
8
9
|
export interface LifecycleOptions {
|
|
@@ -28,7 +28,7 @@ interface Pending {
|
|
|
28
28
|
resolve(allow: boolean): void;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export function createPageVeto<D extends object =
|
|
31
|
+
export function createPageVeto<D extends object = Record<never, never>>(ackTimeout = PAGE_ACK_TIMEOUT): PageVeto<D> {
|
|
32
32
|
let emit: ((data: D & { id: number }, target?: EmitTarget) => number[] | unknown) | null = null;
|
|
33
33
|
let seq = 0;
|
|
34
34
|
const pending = new Map<number, Pending>();
|
|
@@ -216,6 +216,7 @@ export interface DesktopPlugin<Api = any, Events = any> {
|
|
|
216
216
|
*/
|
|
217
217
|
events?: {
|
|
218
218
|
[E in keyof Events]?: (
|
|
219
|
+
|
|
219
220
|
emit: (data: Events[E], target?: EmitTarget) => number[] | void,
|
|
220
221
|
ctx: DesktopContext,
|
|
221
222
|
) => () => void;
|
|
@@ -272,6 +273,8 @@ export function limitExternalOpens(enabled: boolean): void {
|
|
|
272
273
|
lastExternalOpen = -Infinity;
|
|
273
274
|
}
|
|
274
275
|
|
|
275
|
-
export function defineDesktopPlugin<Api, Events =
|
|
276
|
+
export function defineDesktopPlugin<Api, Events = Record<never, never>>(
|
|
277
|
+
plugin: DesktopPlugin<Api, Events>,
|
|
278
|
+
): DesktopPlugin<Api, Events> {
|
|
276
279
|
return plugin;
|
|
277
280
|
}
|
|
@@ -12,7 +12,7 @@ type Output = Pick<TakePhotoOptions, "quality" | "maxWidth" | "maxHeight">;
|
|
|
12
12
|
const jpegQuality = (output: Output) => Math.min(100, Math.max(1, output.quality ?? 90)) / 100;
|
|
13
13
|
|
|
14
14
|
/** The largest size within the limits, keeping the aspect ratio. */
|
|
15
|
-
function
|
|
15
|
+
function fitWithin(width: number, height: number, output: Output): { width: number; height: number } {
|
|
16
16
|
const scale = Math.min(1, (output.maxWidth ?? width) / width, (output.maxHeight ?? height) / height);
|
|
17
17
|
return { width: Math.max(1, Math.round(width * scale)), height: Math.max(1, Math.round(height * scale)) };
|
|
18
18
|
}
|
|
@@ -49,7 +49,7 @@ async function normalized(file: File, output: Output): Promise<Photo> {
|
|
|
49
49
|
return { url: URL.createObjectURL(file), mime: file.type || "application/octet-stream", size: file.size };
|
|
50
50
|
}
|
|
51
51
|
try {
|
|
52
|
-
const size =
|
|
52
|
+
const size = fitWithin(bitmap.width, bitmap.height, output);
|
|
53
53
|
|
|
54
54
|
if (
|
|
55
55
|
file.type === "image/jpeg" &&
|
|
@@ -203,7 +203,7 @@ async function captureWithPreview(options: TakePhotoOptions): Promise<Photo> {
|
|
|
203
203
|
cancel.onclick = () => finish(() => reject(cancelled()));
|
|
204
204
|
shutter.onclick = () => {
|
|
205
205
|
if (!video.videoWidth || !video.videoHeight) return;
|
|
206
|
-
const { width, height } =
|
|
206
|
+
const { width, height } = fitWithin(video.videoWidth, video.videoHeight, options);
|
|
207
207
|
const canvas = document.createElement("canvas");
|
|
208
208
|
canvas.width = width;
|
|
209
209
|
canvas.height = height;
|
|
@@ -198,7 +198,10 @@ export function useFilePicker(): UseFilePicker {
|
|
|
198
198
|
setError(null);
|
|
199
199
|
|
|
200
200
|
return start().then(
|
|
201
|
-
(value) =>
|
|
201
|
+
(value) => {
|
|
202
|
+
setPending(false);
|
|
203
|
+
return value;
|
|
204
|
+
},
|
|
202
205
|
(e: unknown) => {
|
|
203
206
|
setPending(false);
|
|
204
207
|
setError(AkanNativeError.from(e));
|
|
@@ -44,7 +44,7 @@ function dismiss(id: number): void {
|
|
|
44
44
|
|
|
45
45
|
function show(n: ScheduledNotification): void {
|
|
46
46
|
const N = api();
|
|
47
|
-
if (
|
|
47
|
+
if (N?.permission !== "granted") return;
|
|
48
48
|
dismiss(n.id);
|
|
49
49
|
|
|
50
50
|
const notification = new N(n.title, { body: n.body, tag: `akan-native-${n.id}` });
|
|
@@ -315,7 +315,7 @@ export function createDesktopSecureStorage(options: DesktopSecureStorageOptions
|
|
|
315
315
|
store.entries = new Map(readable);
|
|
316
316
|
save(store);
|
|
317
317
|
}
|
|
318
|
-
return { keys: readable.map(([name]) => name).sort() };
|
|
318
|
+
return { keys: readable.map(([name]) => name).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)) };
|
|
319
319
|
},
|
|
320
320
|
clear(_args, ctx) {
|
|
321
321
|
const store = storeFor(ctx);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AkanNativeError } from "../../../packages/core/src/index.ts";
|
|
2
2
|
import type { ShareOptions } from "./index.ts";
|
|
3
3
|
|
|
4
|
-
const NOT_LINKS = ["javascript:", "data:", "blob:", "file:"];
|
|
4
|
+
const NOT_LINKS = ["javascript:", "vbscript:", "data:", "blob:", "file:"];
|
|
5
5
|
|
|
6
6
|
const invalid = (message: string) => new AkanNativeError("INVALID_ARGS", message);
|
|
7
7
|
|
|
@@ -30,7 +30,7 @@ export function createDesktopSingleInstance(
|
|
|
30
30
|
messages.emit(message, true);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
return defineDesktopPlugin<
|
|
33
|
+
return defineDesktopPlugin<Record<never, never>, SingleInstanceEvents>({
|
|
34
34
|
id: "single-instance",
|
|
35
35
|
|
|
36
36
|
launchPhase: "gate",
|
|
@@ -56,7 +56,8 @@ export function createDesktopSingleInstance(
|
|
|
56
56
|
const key = String(++listens);
|
|
57
57
|
messages.listen(
|
|
58
58
|
key,
|
|
59
|
-
(m) =>
|
|
59
|
+
(m) =>
|
|
60
|
+
((emit as (m: SecondInstance, target?: EmitTarget) => number[] | undefined)(m, "focused")?.length ?? 1) > 0,
|
|
60
61
|
);
|
|
61
62
|
return () => messages.unlisten(key);
|
|
62
63
|
},
|
|
@@ -420,7 +420,10 @@ export function createDesktopUpdates(options: DesktopUpdatesOptions = {}): Deskt
|
|
|
420
420
|
}
|
|
421
421
|
const out = new Uint8Array(received);
|
|
422
422
|
let at = 0;
|
|
423
|
-
for (const c of chunks)
|
|
423
|
+
for (const c of chunks) {
|
|
424
|
+
out.set(c, at);
|
|
425
|
+
at += c.length;
|
|
426
|
+
}
|
|
424
427
|
return out;
|
|
425
428
|
};
|
|
426
429
|
|
|
@@ -375,13 +375,13 @@ export function useWindowState(): WindowState | null {
|
|
|
375
375
|
* none prevents it, the window closes as usual. Desktop only: elsewhere it never fires
|
|
376
376
|
* (`appWindow.eventImplementation("closeRequested")` tells). Returns an unregister function.
|
|
377
377
|
*/
|
|
378
|
-
export const onCloseRequested: (handler: VetoHandler<
|
|
378
|
+
export const onCloseRequested: (handler: VetoHandler<Record<never, never>>) => () => void = vetoable(
|
|
379
379
|
(listener) => appWindow.listen("closeRequested", listener),
|
|
380
380
|
(args) => appWindow.answerCloseRequested(args),
|
|
381
381
|
);
|
|
382
382
|
|
|
383
383
|
/** onCloseRequested while mounted; always calls the latest handler. */
|
|
384
|
-
export function useCloseRequested(handler: VetoHandler<
|
|
384
|
+
export function useCloseRequested(handler: VetoHandler<Record<never, never>>): void {
|
|
385
385
|
const latest = React.useRef(handler);
|
|
386
386
|
latest.current = handler;
|
|
387
387
|
React.useEffect(() => onCloseRequested((event) => latest.current(event)), []);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Transcript } from "./Transcript";
|
|
2
|
+
import type { ChatMessage, MessageAttachment, ToolCallResult, TurnLimits } from "./types";
|
|
2
3
|
|
|
3
4
|
export interface CompactOptions {
|
|
4
5
|
/**
|
|
@@ -58,10 +59,17 @@ export class Compaction {
|
|
|
58
59
|
*/
|
|
59
60
|
static tokensOf(messages: readonly ChatMessage[]): number {
|
|
60
61
|
let chars = 0;
|
|
61
|
-
for (const message of messages) if (!message.local) chars += Compaction.#charsOf(message);
|
|
62
|
+
for (const message of Transcript.seen(messages)) if (!message.local) chars += Compaction.#charsOf(message);
|
|
62
63
|
return Math.ceil(chars / 4);
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
/** One tool result as it rides the wire, by the same rule. */
|
|
67
|
+
static resultTokensOf(result: ToolCallResult): number {
|
|
68
|
+
const { attachments, images } = Compaction.#blanked(result.attachments);
|
|
69
|
+
const chars = JSON.stringify(images ? { ...result, attachments } : result).length;
|
|
70
|
+
return Math.ceil(chars / 4) + images * Compaction.imageTokens;
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
/**
|
|
66
74
|
* What the next request is estimated to cost, prompt and all: the provider's own count for the last turn that
|
|
67
75
|
* reported one, plus four characters a token for what the transcript gained since. The provider's number is the
|
|
@@ -88,12 +96,32 @@ export class Compaction {
|
|
|
88
96
|
|
|
89
97
|
static #charsOf(message: ChatMessage): number {
|
|
90
98
|
const posted = message.usage ? { ...message, usage: undefined } : message;
|
|
91
|
-
const
|
|
99
|
+
const own = Compaction.#blanked(message.attachments);
|
|
100
|
+
let images = own.images;
|
|
101
|
+
const toolResults = message.toolResults?.map((result) => {
|
|
102
|
+
const blanked = Compaction.#blanked(result.attachments);
|
|
103
|
+
images += blanked.images;
|
|
104
|
+
return blanked.images ? { ...result, attachments: blanked.attachments } : result;
|
|
105
|
+
});
|
|
92
106
|
if (!images) return JSON.stringify(posted).length;
|
|
93
|
-
|
|
94
|
-
|
|
107
|
+
return (
|
|
108
|
+
JSON.stringify({ ...posted, attachments: own.attachments, toolResults }).length +
|
|
109
|
+
images * Compaction.imageTokens * 4
|
|
95
110
|
);
|
|
96
|
-
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static #blanked(attachments: MessageAttachment[] | undefined): {
|
|
114
|
+
attachments: MessageAttachment[] | undefined;
|
|
115
|
+
images: number;
|
|
116
|
+
} {
|
|
117
|
+
const images = attachments?.filter(Compaction.#isInlinedImage).length ?? 0;
|
|
118
|
+
if (!images) return { attachments, images };
|
|
119
|
+
return {
|
|
120
|
+
attachments: attachments?.map((attachment) =>
|
|
121
|
+
Compaction.#isInlinedImage(attachment) ? { ...attachment, data: "" } : attachment,
|
|
122
|
+
),
|
|
123
|
+
images,
|
|
124
|
+
};
|
|
97
125
|
}
|
|
98
126
|
|
|
99
127
|
static #isInlinedImage(attachment: MessageAttachment): boolean {
|
|
@@ -185,13 +213,16 @@ export class Compaction {
|
|
|
185
213
|
);
|
|
186
214
|
for (const call of message.toolCalls ?? [])
|
|
187
215
|
parts.push(`[called ${call.name} ${Compaction.#clip(JSON.stringify(call.args), 200)}]`);
|
|
188
|
-
for (const result of message.toolResults ?? [])
|
|
216
|
+
for (const result of message.toolResults ?? []) {
|
|
189
217
|
parts.push(
|
|
190
218
|
`[${result.error ? "failed" : "result"} ${result.name}: ${Compaction.#clip(
|
|
191
219
|
result.error ?? JSON.stringify(result.result ?? null),
|
|
192
220
|
400,
|
|
193
221
|
)}]`,
|
|
194
222
|
);
|
|
223
|
+
for (const attachment of result.attachments ?? [])
|
|
224
|
+
parts.push(`[${result.name} returned ${attachment.name}, content not carried into this summary]`);
|
|
225
|
+
}
|
|
195
226
|
if (message.error) parts.push(`[turn failed: ${message.error}]`);
|
|
196
227
|
return `${message.role}: ${parts.join(" ")}`.trimEnd();
|
|
197
228
|
}
|
|
@@ -38,25 +38,32 @@ export class Reference {
|
|
|
38
38
|
*
|
|
39
39
|
* The text is the source of truth for which references a message carries — deleting the token is how somebody
|
|
40
40
|
* takes one back — so the token has to say everything the pointer does. The value is looked up beside it by key.
|
|
41
|
+
* A label's `\`, `[` and `]` are backslash-escaped, as in a markdown link: titles like "[Ops] Minutes" are common.
|
|
41
42
|
*/
|
|
42
43
|
static token(reference: MessageReference): string {
|
|
43
|
-
return `@[${reference.label}](mention:${Reference.keyOf(reference)})`;
|
|
44
|
+
return `@[${reference.label.replace(/[\\[\]]/g, "\\$&")}](mention:${Reference.keyOf(reference)})`;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
/**
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Greedy on the label alone: a label may hold an escaped `\\`, `\[` or `\]`, a backslash escaping nothing (a token
|
|
49
|
+
* written before labels were escaped), and anything else but `]` or an `@[`, where a token starts over. No two
|
|
50
|
+
* alternatives match at the same place, so a long run cannot backtrack. Every other part is a name, an id or a
|
|
51
|
+
* dotted path, none of which holds a bracket.
|
|
52
|
+
*/
|
|
53
|
+
static readonly pattern =
|
|
54
|
+
/@\[((?:\\[\\[\]]|\\(?![\\[\]])|(?!@\[)[^\]\\])*)\]\(mention:([^/)[\]]+)\/([^#)[\]]+)(?:#([^)[\]]*))?\)/g;
|
|
55
|
+
|
|
56
|
+
/** The pointer one match of `pattern` names, its label unescaped. */
|
|
57
|
+
static pointerOf([, label, refName, refId, path]: RegExpMatchArray): MessageReference {
|
|
58
|
+
return { refName, refId, label: label.replace(/\\([\\[\]])/g, "$1"), ...(path ? { path } : {}) };
|
|
59
|
+
}
|
|
48
60
|
|
|
49
61
|
/**
|
|
50
62
|
* The pointers a draft names, in the order it names them. Pointers only — the value lives in `staged` and is
|
|
51
63
|
* joined on by key, so reading the text can never resurrect a value somebody deleted the token for.
|
|
52
64
|
*/
|
|
53
65
|
static parse(text: string): MessageReference[] {
|
|
54
|
-
return [...text.matchAll(Reference.pattern)].map((
|
|
55
|
-
refName,
|
|
56
|
-
refId,
|
|
57
|
-
label,
|
|
58
|
-
...(path ? { path } : {}),
|
|
59
|
-
}));
|
|
66
|
+
return [...text.matchAll(Reference.pattern)].map((match) => Reference.pointerOf(match));
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Compaction } from "./Compaction";
|
|
2
|
+
import type { MessageAttachment, ResourceDiff, ToolCallResult } from "./types";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The ceiling on what one tool call may add to the transcript.
|
|
@@ -11,14 +12,20 @@ import type { ResourceDiff, ToolCallResult } from "./types";
|
|
|
11
12
|
*
|
|
12
13
|
* Clipped rather than dropped, and the note says what happened: a model handed a value it cannot see the end of
|
|
13
14
|
* asks a narrower question, where one handed nothing answers from the field names.
|
|
15
|
+
*
|
|
16
|
+
* A file the call attached is bounded apart: a picture is billed by its pixels, not by the length of its base64, so
|
|
17
|
+
* counting it as characters would clip every one. It meets the per-file ceiling a user's attachment meets instead.
|
|
14
18
|
*/
|
|
15
19
|
export class ToolOutput {
|
|
16
20
|
/** Characters, ~5k tokens: comfortably above `ScreenReader.limit` and far below any provider's window. */
|
|
17
21
|
static readonly limit = 20_000;
|
|
18
22
|
|
|
19
|
-
/**
|
|
23
|
+
/** Bytes of one attached file, as `maxAttachmentBytes` bounds a user's: a provider refuses a turn past it. */
|
|
24
|
+
static readonly attachmentBytes = 4 * 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
/** The estimate `Compaction` places: four characters per token over the JSON, and a flat rate per picture. */
|
|
20
27
|
static tokensOf(result: ToolCallResult): number {
|
|
21
|
-
return
|
|
28
|
+
return Compaction.resultTokensOf(result);
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
static clipped(result: ToolCallResult): ToolCallResult {
|
|
@@ -27,6 +34,21 @@ export class ToolOutput {
|
|
|
27
34
|
...(result.result !== undefined ? { result: ToolOutput.#within(result.result) } : {}),
|
|
28
35
|
...(result.changes?.length ? { changes: result.changes.map((change) => ToolOutput.#change(change)) } : {}),
|
|
29
36
|
...(result.error ? { error: ToolOutput.#text(result.error) } : {}),
|
|
37
|
+
...(result.attachments?.length
|
|
38
|
+
? { attachments: result.attachments.map((attachment) => ToolOutput.#attachment(attachment)) }
|
|
39
|
+
: {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static #attachment(attachment: MessageAttachment): MessageAttachment {
|
|
44
|
+
if (attachment.text) return { ...attachment, text: ToolOutput.#text(attachment.text) };
|
|
45
|
+
|
|
46
|
+
const bytes = Math.floor(((attachment.data?.length ?? 0) * 3) / 4);
|
|
47
|
+
if (bytes <= ToolOutput.attachmentBytes) return attachment;
|
|
48
|
+
return {
|
|
49
|
+
name: attachment.name,
|
|
50
|
+
mimeType: attachment.mimeType,
|
|
51
|
+
text: `[Not attached: ${bytes} bytes, past the ${ToolOutput.attachmentBytes}-byte ceiling on one file a tool may return. Tell the user it was too large to look at.]`,
|
|
30
52
|
};
|
|
31
53
|
}
|
|
32
54
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { MessageAttachment } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What a tool's `run` returns when its answer includes files beside the value: `ToolReturn.attach(result, files)`.
|
|
5
|
+
* `ToolRunner` lifts the files into `ToolCallResult.attachments`, which a backend hands the model as content.
|
|
6
|
+
*/
|
|
7
|
+
export class ToolReturn {
|
|
8
|
+
/** Interned, not an `instanceof`: one page can hold several bundled copies of this package. */
|
|
9
|
+
static readonly brand = Symbol.for("use-agentic.ToolReturn");
|
|
10
|
+
|
|
11
|
+
readonly result: unknown;
|
|
12
|
+
readonly attachments: MessageAttachment[];
|
|
13
|
+
|
|
14
|
+
constructor(result: unknown, attachments: MessageAttachment[]) {
|
|
15
|
+
this.result = result;
|
|
16
|
+
this.attachments = attachments;
|
|
17
|
+
Object.defineProperty(this, ToolReturn.brand, { value: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static attach(result: unknown, attachments: MessageAttachment[]): ToolReturn {
|
|
21
|
+
return new ToolReturn(result, attachments);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static split(value: unknown): { result: unknown; attachments: MessageAttachment[] } {
|
|
25
|
+
if (!ToolReturn.#is(value)) return { result: value, attachments: [] };
|
|
26
|
+
return { result: value.result, attachments: value.attachments };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static #is(value: unknown): value is ToolReturn {
|
|
30
|
+
return typeof value === "object" && value !== null && ToolReturn.brand in value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { AgentAbort } from "./AgentAbort";
|
|
2
2
|
import { AgentProgress, type AgentProgressReport } from "./AgentProgress";
|
|
3
3
|
import { ToolOutput } from "./ToolOutput";
|
|
4
|
+
import { ToolReturn } from "./ToolReturn";
|
|
4
5
|
import type {
|
|
6
|
+
ResourceDiff,
|
|
5
7
|
SurfaceView,
|
|
6
8
|
ToolActivity,
|
|
7
9
|
ToolCallRequest,
|
|
@@ -155,12 +157,7 @@ export class ToolRunner {
|
|
|
155
157
|
const answered = await ask({ callId: call.id, name: call.name, args: call.args, render: entry.card }, signal);
|
|
156
158
|
if ("error" in answered) return { ...base, error: answered.error };
|
|
157
159
|
if (entry.settle !== false) await this.#host.settle?.();
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
...base,
|
|
161
|
-
...(answered.result !== undefined ? { result: answered.result } : {}),
|
|
162
|
-
...(changes.length ? { changes } : {}),
|
|
163
|
-
};
|
|
160
|
+
return ToolRunner.#answered(base, answered.result, this.#surface.diffSince(before));
|
|
164
161
|
}
|
|
165
162
|
|
|
166
163
|
async #execute(
|
|
@@ -180,7 +177,7 @@ export class ToolRunner {
|
|
|
180
177
|
try {
|
|
181
178
|
|
|
182
179
|
if (!this.#stillDecided(call, entry, message)) throw new Error(ToolRunner.#movedError(call.name));
|
|
183
|
-
const
|
|
180
|
+
const returned = await AgentAbort.run(signal, () =>
|
|
184
181
|
AgentProgress.run(
|
|
185
182
|
(report) => this.#host.progress?.({ callId: call.id, report }),
|
|
186
183
|
() => ToolRunner.raced(this.#surface.call(call.name, call.args), signal),
|
|
@@ -188,12 +185,7 @@ export class ToolRunner {
|
|
|
188
185
|
);
|
|
189
186
|
|
|
190
187
|
if (entry.settle !== false) await this.#host.settle?.();
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
...base,
|
|
194
|
-
...(result !== undefined ? { result } : {}),
|
|
195
|
-
...(changes.length ? { changes } : {}),
|
|
196
|
-
};
|
|
188
|
+
return ToolRunner.#answered(base, returned, this.#surface.diffSince(before));
|
|
197
189
|
} catch (error) {
|
|
198
190
|
failed = error instanceof Error ? error.message : String(error);
|
|
199
191
|
return { ...base, error: failed };
|
|
@@ -224,6 +216,16 @@ export class ToolRunner {
|
|
|
224
216
|
);
|
|
225
217
|
}
|
|
226
218
|
|
|
219
|
+
static #answered(base: { id: string; name: string }, returned: unknown, changes: ResourceDiff[]): ToolCallResult {
|
|
220
|
+
const { result, attachments } = ToolReturn.split(returned);
|
|
221
|
+
return {
|
|
222
|
+
...base,
|
|
223
|
+
...(result !== undefined ? { result } : {}),
|
|
224
|
+
...(changes.length ? { changes } : {}),
|
|
225
|
+
...(attachments.length ? { attachments } : {}),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
227
229
|
static #movedError(name: string) {
|
|
228
230
|
return `${name} changed on screen before it ran, so it did not run.`;
|
|
229
231
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatMessage } from "./types";
|
|
1
|
+
import type { ChatMessage, MessageAttachment, ToolCallResult } from "./types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The transcript's own invariant, in one place because three paths break it: a turn stopped between a tool call
|
|
@@ -15,7 +15,43 @@ export class Transcript {
|
|
|
15
15
|
|
|
16
16
|
/** What one turn posts: no host-only message, no unanswered call, no result answering a call nobody sees. */
|
|
17
17
|
static wire(messages: readonly ChatMessage[]): ChatMessage[] {
|
|
18
|
-
return Transcript.sanitize(
|
|
18
|
+
return Transcript.sanitize(
|
|
19
|
+
Transcript.seen(messages.filter((message) => !message.local)).map(Transcript.unmeasured),
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A file a tool returned before the user's latest message, as a note in its place. A picture costs a turn over a
|
|
25
|
+
* thousand tokens and would ride every turn after the one that asked for it; a later question about it calls the
|
|
26
|
+
* tool again, which is why the note says to — and to do it while the picture is on screen, where a tool can reach it.
|
|
27
|
+
*/
|
|
28
|
+
static seen(messages: readonly ChatMessage[]): ChatMessage[] {
|
|
29
|
+
const latest = messages.findLastIndex((message) => message.role === "user" && !message.summary && !message.local);
|
|
30
|
+
if (latest <= 0) return [...messages];
|
|
31
|
+
return messages.map((message, at) =>
|
|
32
|
+
at < latest && message.toolResults?.some(Transcript.#carriesFile)
|
|
33
|
+
? { ...message, toolResults: message.toolResults.map(Transcript.#noted) }
|
|
34
|
+
: message,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static #carriesFile(result: ToolCallResult): boolean {
|
|
39
|
+
return !!result.attachments?.some((attachment) => !attachment.text);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static #noted(result: ToolCallResult): ToolCallResult {
|
|
43
|
+
if (!Transcript.#carriesFile(result)) return result;
|
|
44
|
+
const attachments = result.attachments?.map(
|
|
45
|
+
(attachment): MessageAttachment =>
|
|
46
|
+
attachment.text
|
|
47
|
+
? attachment
|
|
48
|
+
: {
|
|
49
|
+
name: attachment.name,
|
|
50
|
+
mimeType: attachment.mimeType,
|
|
51
|
+
text: `[Seen earlier and no longer attached. To look at it again, call ${result.name} again while it is on screen.]`,
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
return { ...result, attachments };
|
|
19
55
|
}
|
|
20
56
|
|
|
21
57
|
/** A message without the provider's count for its turn — what the wire carries and what a compaction keeps. */
|
|
@@ -64,6 +64,26 @@ of one export.
|
|
|
64
64
|
|
|
65
65
|
Nothing here is stored: the wire carries the bytes for exactly one turn's request.
|
|
66
66
|
|
|
67
|
+
### A tool result's files
|
|
68
|
+
|
|
69
|
+
A `toolResults` entry may carry `attachments` of the same shape — a picture the call looked at, which its `result`
|
|
70
|
+
cannot hold: that reaches the model as JSON text, so a URL in it is read as letters and base64 as a wall of them.
|
|
71
|
+
|
|
72
|
+
```jsonc
|
|
73
|
+
{ "role": "tool", "toolResults": [{ "id": "c3", "name": "viewImage", "result": "Attached card.jpg: 782×962, sent as 782×962 JPEG.",
|
|
74
|
+
"attachments": [{ "name": "card.jpg", "mimeType": "image/jpeg", "data": "/9j/4AAQ…" }] }] }
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The same rules apply, and a provider format with no place for a file inside a tool result is still no reason to
|
|
78
|
+
drop one. Hand it to the model in the nearest place it reads — a chat-completions `tool` message carries text alone,
|
|
79
|
+
so the pictures follow the turn's tool messages in one user message, each named after the call that returned it —
|
|
80
|
+
and replace what it cannot read with a note **in the attachment's place**, as a `text` attachment of the same name:
|
|
81
|
+
a tool message has no text of its own for a note to join.
|
|
82
|
+
|
|
83
|
+
The client sends a tool's file as bytes only in the latest turn. One returned before the user's last message arrives
|
|
84
|
+
as such a note, telling the model to call the tool again, since a picture costs a thousand tokens or more on every
|
|
85
|
+
turn it rides.
|
|
86
|
+
|
|
67
87
|
## Response
|
|
68
88
|
|
|
69
89
|
`200` with a single JSON object:
|
|
@@ -167,6 +167,12 @@ export interface ToolCallResult {
|
|
|
167
167
|
/** What the call changed on the surface — the whole report for a tool that returns nothing. */
|
|
168
168
|
changes?: ResourceDiff[];
|
|
169
169
|
error?: string;
|
|
170
|
+
/**
|
|
171
|
+
* Files the call handed the model — a picture it looked at — in the shape a user's attachment takes. A `result`
|
|
172
|
+
* cannot carry one: it reaches the model as JSON text, so a URL is read as letters and base64 as a wall of them.
|
|
173
|
+
* Outside the `ToolOutput` ceiling, and only the latest turn's ride the wire as bytes (`Transcript.wire`).
|
|
174
|
+
*/
|
|
175
|
+
attachments?: MessageAttachment[];
|
|
170
176
|
}
|
|
171
177
|
|
|
172
178
|
/**
|