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.
Files changed (392) hide show
  1. package/base/baseEnv.ts +5 -2
  2. package/client/device.ts +1 -1
  3. package/client/frameConfig.ts +2 -2
  4. package/client/href.ts +35 -0
  5. package/client/index.ts +1 -0
  6. package/client/makePageProto.tsx +3 -2
  7. package/client/router.ts +6 -4
  8. package/client/rscNavigation.ts +1 -1
  9. package/client/transitionTypes.ts +0 -4
  10. package/client/translator.ts +16 -0
  11. package/client/types.ts +3 -2
  12. package/client/viewportMeta.ts +23 -8
  13. package/client/webManifest.ts +5 -3
  14. package/common/TrustedProxy.ts +14 -0
  15. package/common/codeAgentWire.ts +8 -1
  16. package/common/index.ts +1 -0
  17. package/common/interpolateTranslation.ts +17 -3
  18. package/common/isEmail.ts +1 -1
  19. package/common/localeConfig.ts +10 -4
  20. package/common/markdownSpans.ts +1 -1
  21. package/common/restClient.ts +3 -1
  22. package/common/routeConvention.ts +4 -4
  23. package/common/trimSlashes.ts +11 -0
  24. package/common/types.ts +1 -1
  25. package/constant/fieldInfo.ts +2 -2
  26. package/constant/fieldPlan.ts +80 -0
  27. package/constant/index.ts +3 -0
  28. package/constant/inputParser.ts +148 -0
  29. package/constant/invalidInputError.ts +13 -0
  30. package/constant/serialize.ts +36 -20
  31. package/constant/types.ts +1 -1
  32. package/constant/via.ts +2 -0
  33. package/dictionary/base.dictionary.ts +4 -0
  34. package/dictionary/trans.ts +8 -0
  35. package/document/database.ts +3 -6
  36. package/document/documentSchema.ts +1 -1
  37. package/document/noDocumentError.ts +6 -1
  38. package/fetch/client/fetchClient.ts +2 -1
  39. package/fetch/client/sliceInitHandle.ts +2 -3
  40. package/fetch/fetchType/appliedReturn.type.ts +3 -1
  41. package/fetch/types.ts +4 -2
  42. package/index.ts +2 -0
  43. package/package.json +31 -21
  44. package/server/akanApp.ts +15 -3
  45. package/server/akanServer.ts +34 -14
  46. package/server/artifact/routeClientCache.ts +4 -5
  47. package/server/artifact/routeSeedIndexStore.ts +3 -20
  48. package/server/artifact/routeSeedMatcher.ts +70 -0
  49. package/server/console.ts +1 -1
  50. package/server/di/diLifecycle.ts +16 -7
  51. package/server/imageOptimizer.ts +1 -1
  52. package/server/index.ts +1 -0
  53. package/server/machine/machineAccount.ts +28 -3
  54. package/server/machine/machineToken.ts +27 -3
  55. package/server/mcp/McpDispatcher.ts +2 -3
  56. package/server/mcp/McpExecutionContext.ts +9 -3
  57. package/server/migration/docPath.ts +121 -0
  58. package/server/migration/index.ts +15 -0
  59. package/server/migration/migrationChangeLog.ts +86 -0
  60. package/server/migration/migrationChanges.ts +70 -0
  61. package/server/migration/migrationCommand.ts +179 -0
  62. package/server/migration/migrationContext.ts +53 -0
  63. package/server/migration/migrationDefinition.ts +87 -0
  64. package/server/migration/migrationError.ts +45 -0
  65. package/server/migration/migrationLedger.ts +172 -0
  66. package/server/migration/migrationLock.ts +103 -0
  67. package/server/migration/migrationParams.ts +122 -0
  68. package/server/migration/migrationPulse.ts +39 -0
  69. package/server/migration/migrationRunner.ts +525 -0
  70. package/server/migration/migrationScope.ts +93 -0
  71. package/server/migration/migrationTable.ts +258 -0
  72. package/server/migration/migrations.ts +57 -0
  73. package/server/ops/opsCommand.ts +30 -1
  74. package/server/resolver/CascadeRunner.ts +15 -2
  75. package/server/resolver/signal.resolver.ts +35 -55
  76. package/server/routeState.ts +0 -6
  77. package/server/rscClient.tsx +22 -37
  78. package/server/rscNavigationState.ts +3 -5
  79. package/server/rscScrollRestoration.ts +116 -0
  80. package/server/rscWorker.tsx +12 -19
  81. package/server/sitemap.ts +4 -4
  82. package/server/tunnel/TunnelAgent.ts +9 -1
  83. package/server/webRouter.ts +17 -2
  84. package/service/agent.service.ts +21 -1
  85. package/service/endpointCacheKeys.ts +35 -0
  86. package/service/index.ts +1 -0
  87. package/service/injectInfo.ts +2 -2
  88. package/service/predefinedAdaptor/anthropicLlm.ts +37 -17
  89. package/service/predefinedAdaptor/database.adaptor.ts +15 -0
  90. package/service/predefinedAdaptor/documentTransfer.ts +50 -0
  91. package/service/predefinedAdaptor/endpointCacheControl.adaptor.ts +19 -0
  92. package/service/predefinedAdaptor/index.ts +1 -0
  93. package/service/predefinedAdaptor/llm.adaptor.ts +7 -0
  94. package/service/predefinedAdaptor/openaiDialect.ts +53 -17
  95. package/service/predefinedAdaptor/openaiLlm.ts +1 -1
  96. package/service/predefinedAdaptor/sql/MigrationTables.ts +90 -0
  97. package/service/predefinedAdaptor/sql/RawDocumentTable.ts +200 -0
  98. package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +6 -2
  99. package/service/predefinedAdaptor/sql/SqliteInsightSession.ts +4 -1
  100. package/service/predefinedAdaptor/sql/dialect/postgres.ts +0 -1
  101. package/service/predefinedAdaptor/sql/dialect/sqlite.ts +0 -1
  102. package/service/predefinedAdaptor/sql/types.ts +3 -1
  103. package/service/predefinedAdaptor/storage.adaptor.ts +19 -0
  104. package/signal/endpointCache.ts +23 -7
  105. package/signal/endpointErrorContract.ts +37 -0
  106. package/signal/endpointIdempotency.ts +5 -3
  107. package/signal/endpointInfo.ts +4 -1
  108. package/signal/endpointRateLimit.ts +6 -2
  109. package/signal/exception.ts +4 -1
  110. package/signal/index.ts +1 -0
  111. package/signal/mcp/McpDocument.ts +16 -1
  112. package/signal/mcp/McpUriTemplate.ts +1 -1
  113. package/signal/middlewareChain.ts +105 -0
  114. package/signal/openapi/openapi.ts +46 -2
  115. package/signal/serializer/fetch.serializer.ts +1 -0
  116. package/signal/signalContext.ts +110 -82
  117. package/signal/slice.ts +2 -2
  118. package/signal/trace.ts +8 -2
  119. package/signal/types.ts +13 -0
  120. package/store/action.ts +102 -42
  121. package/store/agent/ScreenImage.ts +166 -0
  122. package/store/agent/ScreenReader.ts +2 -1
  123. package/store/agent/StoreSurfaceSource.ts +41 -2
  124. package/store/agent/index.ts +2 -1
  125. package/store/databaseStateNames.ts +1 -0
  126. package/store/formUploadOf.ts +41 -0
  127. package/store/index.ts +1 -0
  128. package/store/liveInsert.ts +2 -2
  129. package/store/state.ts +9 -1
  130. package/store/stateBuilder.ts +8 -2
  131. package/store/storeInstance.ts +12 -3
  132. package/store/withSelector.ts +2 -2
  133. package/types/client/href.d.ts +11 -0
  134. package/types/client/index.d.ts +1 -0
  135. package/types/client/rscNavigation.d.ts +1 -1
  136. package/types/client/transitionTypes.d.ts +0 -2
  137. package/types/client/translator.d.ts +2 -0
  138. package/types/common/codeAgentWire.d.ts +7 -0
  139. package/types/common/index.d.ts +1 -0
  140. package/types/common/trimSlashes.d.ts +2 -0
  141. package/types/common/types.d.ts +1 -1
  142. package/types/constant/fieldInfo.d.ts +2 -2
  143. package/types/constant/fieldPlan.d.ts +33 -0
  144. package/types/constant/index.d.ts +3 -0
  145. package/types/constant/inputParser.d.ts +11 -0
  146. package/types/constant/invalidInputError.d.ts +8 -0
  147. package/types/constant/types.d.ts +1 -1
  148. package/types/dictionary/base.dictionary.d.ts +1 -1
  149. package/types/dictionary/dictionary.d.ts +3 -3
  150. package/types/dictionary/trans.d.ts +3 -0
  151. package/types/document/database.d.ts +2 -3
  152. package/types/document/documentSchema.d.ts +1 -1
  153. package/types/document/noDocumentError.d.ts +6 -1
  154. package/types/fetch/fetchType/appliedReturn.type.d.ts +3 -1
  155. package/types/fetch/types.d.ts +2 -2
  156. package/types/index.d.ts +5 -0
  157. package/types/server/akanServer.d.ts +1 -1
  158. package/types/server/artifact/routeSeedMatcher.d.ts +8 -0
  159. package/types/server/di/diLifecycle.d.ts +1 -0
  160. package/types/server/index.d.ts +1 -0
  161. package/types/server/machine/machineAccount.d.ts +1 -0
  162. package/types/server/machine/machineToken.d.ts +9 -0
  163. package/types/server/migration/docPath.d.ts +20 -0
  164. package/types/server/migration/index.d.ts +15 -0
  165. package/types/server/migration/migrationChangeLog.d.ts +27 -0
  166. package/types/server/migration/migrationChanges.d.ts +27 -0
  167. package/types/server/migration/migrationCommand.d.ts +33 -0
  168. package/types/server/migration/migrationContext.d.ts +28 -0
  169. package/types/server/migration/migrationDefinition.d.ts +42 -0
  170. package/types/server/migration/migrationError.d.ts +15 -0
  171. package/types/server/migration/migrationLedger.d.ts +51 -0
  172. package/types/server/migration/migrationLock.d.ts +34 -0
  173. package/types/server/migration/migrationParams.d.ts +41 -0
  174. package/types/server/migration/migrationPulse.d.ts +15 -0
  175. package/types/server/migration/migrationRunner.d.ts +93 -0
  176. package/types/server/migration/migrationScope.d.ts +42 -0
  177. package/types/server/migration/migrationTable.d.ts +52 -0
  178. package/types/server/migration/migrations.d.ts +12 -0
  179. package/types/server/migration.d.ts +1 -0
  180. package/types/server/ops/opsCommand.d.ts +10 -1
  181. package/types/server/routeState.d.ts +1 -4
  182. package/types/server/rscNavigationState.d.ts +2 -3
  183. package/types/server/rscScrollRestoration.d.ts +14 -0
  184. package/types/server/sitemap.d.ts +1 -1
  185. package/types/service/agent.service.d.ts +2 -0
  186. package/types/service/endpointCacheKeys.d.ts +8 -0
  187. package/types/service/index.d.ts +1 -0
  188. package/types/service/injectInfo.d.ts +2 -2
  189. package/types/service/predefinedAdaptor/anthropicLlm.d.ts +9 -4
  190. package/types/service/predefinedAdaptor/database.adaptor.d.ts +8 -1
  191. package/types/service/predefinedAdaptor/documentTransfer.d.ts +1 -0
  192. package/types/service/predefinedAdaptor/endpointCacheControl.adaptor.d.ts +9 -0
  193. package/types/service/predefinedAdaptor/index.d.ts +1 -0
  194. package/types/service/predefinedAdaptor/llm.adaptor.d.ts +7 -0
  195. package/types/service/predefinedAdaptor/openaiDialect.d.ts +20 -5
  196. package/types/service/predefinedAdaptor/sql/MigrationTables.d.ts +17 -0
  197. package/types/service/predefinedAdaptor/sql/RawDocumentTable.d.ts +38 -0
  198. package/types/service/predefinedAdaptor/sql/types.d.ts +1 -0
  199. package/types/service/predefinedAdaptor/storage.adaptor.d.ts +8 -0
  200. package/types/signal/endpointCache.d.ts +2 -0
  201. package/types/signal/endpointErrorContract.d.ts +10 -0
  202. package/types/signal/endpointIdempotency.d.ts +1 -0
  203. package/types/signal/endpointRateLimit.d.ts +1 -1
  204. package/types/signal/exception.d.ts +4 -1
  205. package/types/signal/index.d.ts +1 -0
  206. package/types/signal/middlewareChain.d.ts +11 -0
  207. package/types/signal/signalContext.d.ts +2 -6
  208. package/types/signal/slice.d.ts +1 -1
  209. package/types/signal/trace.d.ts +1 -1
  210. package/types/signal/types.d.ts +12 -0
  211. package/types/store/action.d.ts +2 -0
  212. package/types/store/agent/ScreenImage.d.ts +27 -0
  213. package/types/store/agent/StoreSurfaceSource.d.ts +1 -1
  214. package/types/store/agent/index.d.ts +2 -1
  215. package/types/store/databaseStateNames.d.ts +1 -0
  216. package/types/store/formUploadOf.d.ts +9 -0
  217. package/types/store/index.d.ts +1 -0
  218. package/types/store/state.d.ts +7 -1
  219. package/types/store/stateBuilder.d.ts +2 -1
  220. package/types/store/storeInstance.d.ts +2 -2
  221. package/types/store/withSelector.d.ts +1 -1
  222. package/types/ui/Agent/Composer.d.ts +2 -0
  223. package/types/ui/Agent/Menu.d.ts +2 -0
  224. package/types/ui/CsrImage.d.ts +2 -0
  225. package/types/ui/Data/CardList.d.ts +1 -1
  226. package/types/ui/Data/Dashboard.d.ts +1 -1
  227. package/types/ui/Data/Insight.d.ts +1 -1
  228. package/types/ui/Data/Item.d.ts +2 -2
  229. package/types/ui/Data/ListContainer.d.ts +1 -1
  230. package/types/ui/Data/Pagination.d.ts +1 -1
  231. package/types/ui/Data/TableList.d.ts +1 -1
  232. package/types/ui/Field/Relation.d.ts +13 -10
  233. package/types/ui/Field/index.d.ts +4 -4
  234. package/types/ui/Field/useRelationOptions.d.ts +3 -1
  235. package/types/ui/Field/useRelationWindow.d.ts +14 -0
  236. package/types/ui/Image.d.ts +3 -1
  237. package/types/ui/Load/View.d.ts +1 -1
  238. package/types/ui/Pagination.d.ts +1 -1
  239. package/types/ui/Signal/Arg.d.ts +1 -1
  240. package/types/ui/System/CSR.d.ts +1 -1
  241. package/types/ui/System/Client.d.ts +1 -1
  242. package/types/ui/System/SSR.d.ts +1 -1
  243. package/types/vendor/@akanjs/native/packages/core/src/plugin.d.ts +3 -3
  244. package/types/vendor/@akanjs/native/packages/desktop/src/page-veto.d.ts +1 -1
  245. package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +1 -1
  246. package/types/vendor/@akanjs/native/plugins/auth-session/src/desktop.d.ts +2 -2
  247. package/types/vendor/@akanjs/native/plugins/auth-session/src/index.d.ts +1 -1
  248. package/types/vendor/@akanjs/native/plugins/autostart/src/desktop.d.ts +2 -2
  249. package/types/vendor/@akanjs/native/plugins/autostart/src/index.d.ts +1 -1
  250. package/types/vendor/@akanjs/native/plugins/badge/src/desktop.d.ts +1 -1
  251. package/types/vendor/@akanjs/native/plugins/badge/src/index.d.ts +1 -1
  252. package/types/vendor/@akanjs/native/plugins/badge/src/web.d.ts +1 -1
  253. package/types/vendor/@akanjs/native/plugins/biometric/src/index.d.ts +1 -1
  254. package/types/vendor/@akanjs/native/plugins/camera/src/desktop.d.ts +1 -1
  255. package/types/vendor/@akanjs/native/plugins/camera/src/index.d.ts +1 -1
  256. package/types/vendor/@akanjs/native/plugins/camera/src/web.d.ts +1 -1
  257. package/types/vendor/@akanjs/native/plugins/clipboard/src/desktop.d.ts +2 -2
  258. package/types/vendor/@akanjs/native/plugins/clipboard/src/index.d.ts +1 -1
  259. package/types/vendor/@akanjs/native/plugins/clipboard/src/web.d.ts +1 -1
  260. package/types/vendor/@akanjs/native/plugins/contacts/src/index.d.ts +1 -1
  261. package/types/vendor/@akanjs/native/plugins/device/src/desktop.d.ts +2 -2
  262. package/types/vendor/@akanjs/native/plugins/device/src/index.d.ts +1 -1
  263. package/types/vendor/@akanjs/native/plugins/device/src/web.d.ts +1 -1
  264. package/types/vendor/@akanjs/native/plugins/dialog/src/desktop.d.ts +1 -1
  265. package/types/vendor/@akanjs/native/plugins/dialog/src/index.d.ts +1 -1
  266. package/types/vendor/@akanjs/native/plugins/dialog/src/web.d.ts +1 -1
  267. package/types/vendor/@akanjs/native/plugins/dock/src/desktop.d.ts +1 -1
  268. package/types/vendor/@akanjs/native/plugins/dock/src/index.d.ts +1 -1
  269. package/types/vendor/@akanjs/native/plugins/file-picker/src/desktop.d.ts +2 -2
  270. package/types/vendor/@akanjs/native/plugins/file-picker/src/web.d.ts +1 -1
  271. package/types/vendor/@akanjs/native/plugins/filesystem/src/desktop.d.ts +2 -2
  272. package/types/vendor/@akanjs/native/plugins/filesystem/src/web.d.ts +1 -1
  273. package/types/vendor/@akanjs/native/plugins/haptics/src/index.d.ts +1 -1
  274. package/types/vendor/@akanjs/native/plugins/haptics/src/web.d.ts +1 -1
  275. package/types/vendor/@akanjs/native/plugins/http/src/desktop.d.ts +2 -2
  276. package/types/vendor/@akanjs/native/plugins/http/src/web.d.ts +1 -1
  277. package/types/vendor/@akanjs/native/plugins/keep-awake/src/desktop.d.ts +2 -2
  278. package/types/vendor/@akanjs/native/plugins/keep-awake/src/index.d.ts +1 -1
  279. package/types/vendor/@akanjs/native/plugins/keep-awake/src/web.d.ts +1 -1
  280. package/types/vendor/@akanjs/native/plugins/opener/src/desktop.d.ts +2 -2
  281. package/types/vendor/@akanjs/native/plugins/opener/src/index.d.ts +1 -1
  282. package/types/vendor/@akanjs/native/plugins/opener/src/web.d.ts +1 -1
  283. package/types/vendor/@akanjs/native/plugins/preferences/src/desktop.d.ts +1 -1
  284. package/types/vendor/@akanjs/native/plugins/preferences/src/index.d.ts +1 -1
  285. package/types/vendor/@akanjs/native/plugins/preferences/src/web.d.ts +1 -1
  286. package/types/vendor/@akanjs/native/plugins/secure-storage/src/desktop.d.ts +2 -2
  287. package/types/vendor/@akanjs/native/plugins/secure-storage/src/index.d.ts +1 -1
  288. package/types/vendor/@akanjs/native/plugins/share/src/index.d.ts +1 -1
  289. package/types/vendor/@akanjs/native/plugins/share/src/web.d.ts +1 -1
  290. package/types/vendor/@akanjs/native/plugins/single-instance/src/desktop.d.ts +2 -2
  291. package/types/vendor/@akanjs/native/plugins/splash-screen/src/index.d.ts +1 -1
  292. package/types/vendor/@akanjs/native/plugins/splash-screen/src/web.d.ts +1 -1
  293. package/types/vendor/@akanjs/native/plugins/sqlite/src/desktop.d.ts +2 -2
  294. package/types/vendor/@akanjs/native/plugins/toast/src/index.d.ts +1 -1
  295. package/types/vendor/@akanjs/native/plugins/toast/src/web.d.ts +1 -1
  296. package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +2 -2
  297. package/types/vendor/@akanjs/native/plugins/window-state/src/desktop.d.ts +2 -2
  298. package/types/vendor/@akanjs/native/plugins/window-state/src/index.d.ts +1 -1
  299. package/types/vendor/use-agentic/Compaction.d.ts +3 -1
  300. package/types/vendor/use-agentic/Reference.d.ts +9 -1
  301. package/types/vendor/use-agentic/ToolOutput.d.ts +6 -1
  302. package/types/vendor/use-agentic/ToolReturn.d.ts +18 -0
  303. package/types/vendor/use-agentic/Transcript.d.ts +7 -0
  304. package/types/vendor/use-agentic/index.d.ts +1 -0
  305. package/types/vendor/use-agentic/types.d.ts +6 -0
  306. package/types/webkit/useDebounce.d.ts +1 -1
  307. package/ui/Agent/Chat.tsx +1 -0
  308. package/ui/Agent/Composer.tsx +2 -0
  309. package/ui/Agent/Menu.tsx +5 -1
  310. package/ui/Agent/agentSessionOf.ts +15 -8
  311. package/ui/Agent/mentionDraft.ts +2 -3
  312. package/ui/Agent/useReferenceMenu.ts +1 -0
  313. package/ui/Agent/useSlashMenu.ts +1 -0
  314. package/ui/Clipboard.tsx +1 -0
  315. package/ui/CsrImage.tsx +3 -1
  316. package/ui/Data/CardList.tsx +1 -1
  317. package/ui/Data/Dashboard.tsx +1 -1
  318. package/ui/Data/Insight.tsx +1 -1
  319. package/ui/Data/Item.tsx +5 -2
  320. package/ui/Data/ListContainer.tsx +2 -2
  321. package/ui/Data/Pagination.tsx +1 -1
  322. package/ui/Data/TableList.tsx +1 -1
  323. package/ui/DraggableList.tsx +1 -0
  324. package/ui/Field/Collection.tsx +4 -0
  325. package/ui/Field/Relation.tsx +33 -14
  326. package/ui/Field/useRelationOptions.ts +33 -5
  327. package/ui/Field/useRelationWindow.ts +50 -0
  328. package/ui/Image.tsx +4 -4
  329. package/ui/Input.tsx +1 -0
  330. package/ui/Layout/Sider.tsx +1 -1
  331. package/ui/Link/Anchor.tsx +7 -5
  332. package/ui/Load/Units.tsx +17 -7
  333. package/ui/Load/View.tsx +2 -2
  334. package/ui/Model/Edit.tsx +1 -1
  335. package/ui/Model/EditModal.tsx +11 -5
  336. package/ui/Model/New.tsx +1 -1
  337. package/ui/Model/SureToRemove.tsx +1 -0
  338. package/ui/Model/ViewEditModal.tsx +17 -5
  339. package/ui/Model/ViewModal.tsx +2 -2
  340. package/ui/ObjectId.tsx +5 -1
  341. package/ui/Pagination.tsx +1 -1
  342. package/ui/Signal/Arg.tsx +1 -3
  343. package/ui/Signal/Doc.tsx +5 -1
  344. package/ui/System/CSR.tsx +1 -1
  345. package/ui/System/Client.tsx +1 -1
  346. package/ui/System/Reconnect.tsx +1 -1
  347. package/ui/System/SSR.tsx +3 -36
  348. package/ui/ToggleSelect.tsx +2 -0
  349. package/ui/styles.css +10 -103
  350. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeBridge.swift +3 -2
  351. package/vendor/@akanjs/native/packages/cli/src/lib/addons.ts +4 -1
  352. package/vendor/@akanjs/native/packages/cli/src/lib/apk.ts +2 -2
  353. package/vendor/@akanjs/native/packages/cli/src/lib/codegen.ts +1 -1
  354. package/vendor/@akanjs/native/packages/cli/src/lib/csp.ts +4 -3
  355. package/vendor/@akanjs/native/packages/cli/src/lib/googleservices.ts +7 -1
  356. package/vendor/@akanjs/native/packages/cli/src/lib/hmr.ts +8 -5
  357. package/vendor/@akanjs/native/packages/cli/src/lib/html.ts +18 -3
  358. package/vendor/@akanjs/native/packages/cli/src/lib/manifestmerge.ts +1 -1
  359. package/vendor/@akanjs/native/packages/cli/src/lib/plist.ts +2 -4
  360. package/vendor/@akanjs/native/packages/cli/src/lib/toolchains.ts +1 -1
  361. package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +10 -4
  362. package/vendor/@akanjs/native/packages/cli/src/lib/xml.ts +4 -3
  363. package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +3 -3
  364. package/vendor/@akanjs/native/packages/core/src/kernel.ts +3 -3
  365. package/vendor/@akanjs/native/packages/core/src/live.ts +1 -0
  366. package/vendor/@akanjs/native/packages/core/src/plugin.ts +5 -3
  367. package/vendor/@akanjs/native/packages/desktop/src/host.ts +13 -5
  368. package/vendor/@akanjs/native/packages/desktop/src/lifecycle.ts +1 -0
  369. package/vendor/@akanjs/native/packages/desktop/src/page-veto.ts +1 -1
  370. package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +4 -1
  371. package/vendor/@akanjs/native/plugins/camera/src/web.ts +3 -3
  372. package/vendor/@akanjs/native/plugins/file-picker/src/index.ts +4 -1
  373. package/vendor/@akanjs/native/plugins/local-notifications/src/web.ts +1 -1
  374. package/vendor/@akanjs/native/plugins/secure-storage/src/desktop.ts +1 -1
  375. package/vendor/@akanjs/native/plugins/share/src/options.ts +1 -1
  376. package/vendor/@akanjs/native/plugins/single-instance/src/desktop.ts +3 -2
  377. package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +4 -1
  378. package/vendor/@akanjs/native/plugins/window/src/index.ts +2 -2
  379. package/vendor/use-agentic/Compaction.ts +38 -7
  380. package/vendor/use-agentic/Reference.ts +16 -9
  381. package/vendor/use-agentic/ToolOutput.ts +25 -3
  382. package/vendor/use-agentic/ToolReturn.ts +32 -0
  383. package/vendor/use-agentic/ToolRunner.ts +15 -13
  384. package/vendor/use-agentic/Transcript.ts +38 -2
  385. package/vendor/use-agentic/WIRE.md +20 -0
  386. package/vendor/use-agentic/index.ts +1 -0
  387. package/vendor/use-agentic/types.ts +6 -0
  388. package/webkit/McpAppAgent.ts +12 -2
  389. package/webkit/RenderLayer.tsx +23 -4
  390. package/webkit/useDebounce.ts +23 -14
  391. package/client/viewTransition.ts +0 -10
  392. 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
- ...(field.isClass && fileUploadRefName && ConstantRegistry.getRefName(field.modelRef) === fileUploadRefName
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 form = (this.get() as { [key: string]: any })[names.modelForm] as { [key: string]: any };
408
+ const formId = ((this.get() as { [key: string]: any })[names.modelForm] as { id?: string }).id;
395
409
  if (!fileList.length) return;
396
- const files = await (fetch[names.addModelFiles] as (...args: any) => Promise<ProtoFile[]>)(
397
- fileList,
398
- form.id,
399
- );
400
- if (field.isArray) {
401
- const idx = index ?? (form[namesOfField.field] as ProtoLightFile[]).length;
402
- this.set((state: { [key: string]: { [key: string]: ProtoLightFile[] } }) => {
403
- state[names.modelForm][namesOfField.field] = [
404
- ...(form[namesOfField.field] as ProtoLightFile[]).slice(0, idx),
405
- ...files,
406
- ...(form[namesOfField.field] as ProtoLightFile[]).slice(idx),
407
- ];
408
- });
409
- } else {
410
- this.set((state: { [key: string]: { [key: string]: ProtoLightFile | null } }) => {
411
- state[names.modelForm][namesOfField.field] = files[0];
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 (field.isArray)
428
- this.set((state: { [key: string]: { [key: string]: ProtoLightFile[] } }) => {
429
- state[names.modelForm][namesOfField.field] = state[names.modelForm][namesOfField.field].map(
430
- (file) => (file.id === currentFile.id ? currentFile : file),
431
- );
432
- });
433
- else
434
- this.set((state: { [key: string]: { [key: string]: ProtoLightFile | null } }) => {
435
- state[names.modelForm][namesOfField.field] = currentFile;
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
- this.set({ [names.modelSubmit]: { ...modelSubmit, disabled: !modelInput || disabled } });
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]: new Date(),
999
- [namesOfSlice.lastPageOfModel]: Math.max(Math.floor((modelInsight.count - 1) / (limit || 20)) + 1, 1),
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
- [namesOfSlice.lastPageOfModel]: Math.max(Math.floor((modelInsight.count - 1) / limit) + 1, 1),
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
- [namesOfSlice.lastPageOfModel]: Math.max(Math.floor((modelInsight.count - 1) / limitOfModel) + 1, 1),
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
- if (event.op === "invalidate") {
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 = (currentState[namesOfSlice.limitOfModel] as number) || 20;
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
- [namesOfSlice.lastPageOfModel]: Math.max(Math.floor((count - 1) / limit) + 1, 1),
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 !== "#" && !href.startsWith("javascript:") && text !== href) this.#buffer += ` (${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",
@@ -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
@@ -6,6 +6,7 @@ export * from "./baseSt";
6
6
  export * from "./databaseStateNames";
7
7
  export * from "./draftStore";
8
8
  export * from "./formSetterNames";
9
+ export * from "./formUploadOf";
9
10
  export * from "./liveInsert";
10
11
  export * from "./rootStore";
11
12
  export * from "./sliceRole";
@@ -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 => {