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
@@ -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("${applicationId}", appId)}`).join("\n")}
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("${applicationId}", appId)}`).join("\n")}
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("${applicationId}", appId)}`).join("\n")}
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 value = retryAfter?.replace(/^[ \t]+|[ \t]+$/g, "") ?? "";
457
- if (!/^[0-9]+$/.test(value)) return null;
458
- const seconds = Number(value);
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
 
@@ -12,6 +12,7 @@ export interface LiveValue<T> {
12
12
  */
13
13
  export function createLiveValue<T>(
14
14
  initial: T,
15
+
15
16
  start?: (set: (value: T) => void) => (() => void) | void,
16
17
  equals: (a: T, b: T) => boolean = Object.is,
17
18
  ): LiveValue<T> {
@@ -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 = {}> = PluginMethods<Api> & PluginHandle<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 = {}>(impl: WebPlugin<Api, Events>): WebPlugin<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("abort", () => resolve((cancels++, undefined)), { once: true }),
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 ??= setTimeout(() => {
383
- continuation = null;
384
- drain();
385
- }, 0);
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 = {}>(ackTimeout = PAGE_ACK_TIMEOUT): PageVeto<D> {
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 = {}>(plugin: DesktopPlugin<Api, Events>): DesktopPlugin<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 fit(width: number, height: number, output: Output): { width: number; height: number } {
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 = fit(bitmap.width, bitmap.height, output);
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 } = fit(video.videoWidth, video.videoHeight, options);
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) => (setPending(false), 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 (!N || N.permission !== "granted") return;
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<{}, SingleInstanceEvents>({
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) => ((emit as (m: SecondInstance, target?: EmitTarget) => number[] | void)(m, "focused")?.length ?? 1) > 0,
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) out.set(c, at), (at += c.length);
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<{}>) => () => void = vetoable(
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<{}>): void {
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 type { ChatMessage, MessageAttachment, TurnLimits } from "./types";
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 images = message.attachments?.filter(Compaction.#isInlinedImage).length ?? 0;
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
- const attachments = message.attachments?.map((attachment) =>
94
- Compaction.#isInlinedImage(attachment) ? { ...attachment, data: "" } : attachment,
107
+ return (
108
+ JSON.stringify({ ...posted, attachments: own.attachments, toolResults }).length +
109
+ images * Compaction.imageTokens * 4
95
110
  );
96
- return JSON.stringify({ ...posted, attachments }).length + images * Compaction.imageTokens * 4;
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
- /** Greedy on the label alone: a label may hold anything but `]`, and every other part is a name or an id. */
47
- static readonly pattern = /@\[([^\]]*)\]\(mention:([^/)]+)\/([^#)]+)(?:#([^)]*))?\)/g;
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(([, label, refName, refId, path]) => ({
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 type { ResourceDiff, ToolCallResult } from "./types";
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
- /** Four characters per token, over the JSON the result rides as — the same estimate `Compaction` places. */
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 Math.ceil(JSON.stringify(result).length / 4);
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
- const changes = this.#surface.diffSince(before);
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 result = await AgentAbort.run(signal, () =>
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
- const changes = this.#surface.diffSince(before);
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(messages.filter((message) => !message.local).map(Transcript.unmeasured));
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:
@@ -13,6 +13,7 @@ export * from "./Reference";
13
13
  export * from "./sharedContext";
14
14
  export * from "./surfaceContext";
15
15
  export * from "./ToolOutput";
16
+ export * from "./ToolReturn";
16
17
  export * from "./ToolRunner";
17
18
  export * from "./Transcript";
18
19
  export * from "./types";
@@ -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
  /**