@notis_ai/cli 0.2.18 → 0.2.20

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 (395) hide show
  1. package/README.md +542 -146
  2. package/bin/check-runtime.js +15 -0
  3. package/bin/notis.js +2 -0
  4. package/config/notis_app_design_rules.json +320 -0
  5. package/config/portal-route-roots.json +45 -0
  6. package/dist/agent-hooks/notis-agent-hook.mjs +42325 -18273
  7. package/dist/base-skills/notis-cli/.notis-base-skill.json +1 -0
  8. package/dist/base-skills/notis-cli/SKILL.md +51 -280
  9. package/dist/base-skills/notis-cli/references/app-delivery.md +17 -0
  10. package/dist/base-skills/notis-cli/references/intelligence.md +94 -0
  11. package/dist/base-skills/notis-cli/references/native-databases.md +21 -0
  12. package/dist/base-skills/notis-cli/references/tool-examples.md +56 -0
  13. package/dist/base-skills/notis-cli/references/troubleshooting.md +67 -0
  14. package/dist/base-skills/notis-query/.notis-base-skill.json +1 -0
  15. package/dist/base-skills/notis-query/SKILL.md +35 -699
  16. package/dist/base-skills/notis-query/references/database-discovery.md +59 -0
  17. package/dist/base-skills/notis-query/references/documents.md +74 -0
  18. package/dist/base-skills/notis-query/references/query.md +76 -0
  19. package/dist/base-skills/notis-spaces/.notis-base-skill.json +1 -0
  20. package/dist/base-skills/notis-spaces/SKILL.md +218 -0
  21. package/dist/base-skills/notis-spaces/references/architecture.md +116 -0
  22. package/dist/base-skills/notis-spaces/references/context.md +81 -0
  23. package/dist/base-skills/notis-spaces/references/design.md +141 -0
  24. package/dist/base-skills/notis-spaces/references/fixtures.md +29 -0
  25. package/dist/base-skills/notis-spaces/references/memory.md +58 -0
  26. package/dist/base-skills/notis-spaces/references/reading.md +73 -0
  27. package/dist/base-skills/notis-spaces/references/release.md +120 -0
  28. package/dist/base-skills/notis-spaces/references/reports.md +77 -0
  29. package/dist/base-skills/notis-spaces/references/sdk.md +273 -0
  30. package/dist/base-skills/notis-spaces/references/store.md +118 -0
  31. package/dist/base-skills/notis-spaces/references/troubleshooting.md +28 -0
  32. package/dist/base-skills/notis-spaces/references/views.md +229 -0
  33. package/{template/packages → dist}/sdk/package.json +9 -10
  34. package/dist/sdk/src/agentContext.ts +36 -0
  35. package/dist/sdk/src/bundleModule.ts +175 -0
  36. package/dist/sdk/src/components/Dialog.tsx +103 -0
  37. package/dist/sdk/src/components/DocumentEditor.tsx +113 -0
  38. package/dist/sdk/src/components/DocumentPage.tsx +184 -0
  39. package/{template/packages → dist}/sdk/src/components/Markdown.tsx +9 -3
  40. package/{template/packages → dist}/sdk/src/components/MarkdownEditor.tsx +26 -5
  41. package/{template/packages → dist}/sdk/src/components/MultiSelectActionBar.tsx +95 -12
  42. package/{template/packages → dist}/sdk/src/components/MultiSelectCheckbox.tsx +4 -2
  43. package/dist/sdk/src/components/NotisCommentBoundary.tsx +172 -0
  44. package/{template/packages → dist}/sdk/src/components/NotisSelectionBoundary.tsx +9 -5
  45. package/dist/sdk/src/components/RecordComponents.tsx +111 -0
  46. package/dist/sdk/src/components/Skeleton.tsx +38 -0
  47. package/dist/sdk/src/config.ts +31 -0
  48. package/{template/packages → dist}/sdk/src/documents.ts +21 -6
  49. package/dist/sdk/src/hooks/useAction.ts +28 -0
  50. package/dist/sdk/src/hooks/useAgentContext.ts +23 -0
  51. package/{template/packages → dist}/sdk/src/hooks/useBackend.ts +3 -2
  52. package/dist/sdk/src/hooks/useCloudComputer.ts +94 -0
  53. package/dist/sdk/src/hooks/useCloudComputerShell.ts +114 -0
  54. package/{template/packages → dist}/sdk/src/hooks/useCollectionInteractions.ts +157 -147
  55. package/dist/sdk/src/hooks/useDatabaseSchema.ts +50 -0
  56. package/{template/packages → dist}/sdk/src/hooks/useDatabaseSubscription.ts +2 -2
  57. package/dist/sdk/src/hooks/useDocument.ts +51 -0
  58. package/{template/packages → dist}/sdk/src/hooks/useDocuments.ts +25 -59
  59. package/{template/packages → dist}/sdk/src/hooks/useHandover.ts +7 -7
  60. package/dist/sdk/src/hooks/useLongPressSelection.ts +79 -0
  61. package/dist/sdk/src/hooks/useMarqueeSelection.ts +194 -0
  62. package/{template/packages → dist}/sdk/src/hooks/useMultiSelect.ts +2 -8
  63. package/dist/sdk/src/hooks/useNativeDocumentBody.ts +50 -0
  64. package/dist/sdk/src/hooks/useNativeDocuments.ts +143 -0
  65. package/{template/packages → dist}/sdk/src/hooks/useNotis.ts +6 -1
  66. package/dist/sdk/src/hooks/useNotisNavigation.ts +70 -0
  67. package/dist/sdk/src/hooks/useQuery.ts +81 -0
  68. package/dist/sdk/src/hooks/useToolQuery.ts +12 -0
  69. package/dist/sdk/src/hooks/useTopBarSearch.ts +102 -0
  70. package/dist/sdk/src/hooks/useTopBarTitleLeaf.ts +75 -0
  71. package/{template/packages → dist}/sdk/src/hooks/useUpsertDocument.ts +1 -1
  72. package/dist/sdk/src/hooks/useViewParams.ts +195 -0
  73. package/dist/sdk/src/hooks/useViewerReads.ts +129 -0
  74. package/{template/packages → dist}/sdk/src/index.ts +64 -1
  75. package/{template/packages → dist}/sdk/src/interactions/actions.ts +14 -1
  76. package/{template/packages → dist}/sdk/src/interactions/shortcuts.tsx +129 -23
  77. package/dist/sdk/src/interactions/visibility.ts +13 -0
  78. package/{template/packages → dist}/sdk/src/interactions.ts +5 -1
  79. package/dist/sdk/src/nativeData.ts +39 -0
  80. package/{template/packages → dist}/sdk/src/provider.tsx +9 -4
  81. package/dist/sdk/src/queryCache.ts +292 -0
  82. package/dist/sdk/src/queryErrors.ts +51 -0
  83. package/dist/sdk/src/runtime.ts +675 -0
  84. package/dist/sdk/src/runtimeModules.ts +33 -0
  85. package/dist/sdk/src/space.ts +442 -0
  86. package/dist/sdk/src/standardViewParams.ts +36 -0
  87. package/{template/packages → dist}/sdk/src/styles.css +81 -16
  88. package/dist/sdk/src/tailwind.ts +66 -0
  89. package/dist/sdk/src/ui/charts/RenderChartData.tsx +25 -0
  90. package/dist/sdk/src/ui/engine/core.ts +392 -0
  91. package/dist/sdk/src/ui/engine/dom.ts +183 -0
  92. package/dist/sdk/src/ui/engine/hooks.ts +195 -0
  93. package/dist/sdk/src/ui/engine/instance.ts +63 -0
  94. package/dist/sdk/src/ui/engine/keycapContext.ts +2 -0
  95. package/dist/sdk/src/ui/engine/registry.ts +262 -0
  96. package/dist/sdk/src/ui/engine/types.ts +62 -0
  97. package/dist/sdk/src/ui/icons.tsx +91 -0
  98. package/dist/sdk/src/ui/util.ts +83 -0
  99. package/dist/sdk/src/viewPeriod.ts +26 -0
  100. package/dist/sdk/src/vite.ts +92 -0
  101. package/dist/sdk/src/words/actions.ts +295 -0
  102. package/dist/sdk/src/words/clashes.ts +158 -0
  103. package/dist/sdk/src/words/format.ts +136 -0
  104. package/dist/sdk/src/words/index.ts +13 -0
  105. package/dist/sdk/src/words/keymap.ts +72 -0
  106. package/dist/sdk/src/words/keys.ts +297 -0
  107. package/dist/sdk/src/words/shell.ts +46 -0
  108. package/dist/sdk/src/words/types.ts +65 -0
  109. package/dist/skill-sync/index.js +832 -151
  110. package/dist/skill-sync/index.js.map +4 -4
  111. package/dist/skill-sync-worker.mjs +3941 -0
  112. package/dist/space-harness/host.js +1460 -0
  113. package/dist/space-sdk/dist/chunk-ZSN57ATH.js +5 -0
  114. package/dist/space-sdk/dist/index.js +15 -0
  115. package/dist/space-sdk/dist/ui.js +18 -0
  116. package/dist/space-sdk/package.json +33 -0
  117. package/dist/space-sdk/src/agentContext.d.ts +36 -0
  118. package/dist/space-sdk/src/components/Dialog.d.ts +20 -0
  119. package/dist/space-sdk/src/components/DocumentEditor.d.ts +46 -0
  120. package/dist/space-sdk/src/components/DocumentPage.d.ts +53 -0
  121. package/dist/space-sdk/src/components/Markdown.d.ts +18 -0
  122. package/dist/space-sdk/src/components/MarkdownEditor.d.ts +11 -0
  123. package/dist/space-sdk/src/components/MultiSelectActionBar.d.ts +31 -0
  124. package/dist/space-sdk/src/components/MultiSelectCheckbox.d.ts +14 -0
  125. package/dist/space-sdk/src/components/MultiSelectDragOverlay.d.ts +8 -0
  126. package/dist/space-sdk/src/components/NotisCommentBoundary.d.ts +36 -0
  127. package/dist/space-sdk/src/components/NotisSelectionBoundary.d.ts +16 -0
  128. package/dist/space-sdk/src/components/RecordComponents.d.ts +64 -0
  129. package/dist/space-sdk/src/components/ShortcutHints.d.ts +12 -0
  130. package/dist/space-sdk/src/components/Skeleton.d.ts +23 -0
  131. package/dist/space-sdk/src/config.ts +31 -0
  132. package/dist/space-sdk/src/documents.d.ts +59 -0
  133. package/dist/space-sdk/src/hooks/useAction.d.ts +8 -0
  134. package/dist/space-sdk/src/hooks/useActiveResource.d.ts +3 -0
  135. package/dist/space-sdk/src/hooks/useAgentContext.d.ts +7 -0
  136. package/dist/space-sdk/src/hooks/useBackend.d.ts +23 -0
  137. package/dist/space-sdk/src/hooks/useCloudComputer.d.ts +45 -0
  138. package/dist/space-sdk/src/hooks/useCloudComputerShell.d.ts +49 -0
  139. package/dist/space-sdk/src/hooks/useCollectionInteractions.d.ts +118 -0
  140. package/dist/space-sdk/src/hooks/useDatabaseSchema.d.ts +14 -0
  141. package/dist/space-sdk/src/hooks/useDatabaseSubscription.d.ts +26 -0
  142. package/dist/space-sdk/src/hooks/useDocument.d.ts +22 -0
  143. package/dist/space-sdk/src/hooks/useDocuments.d.ts +31 -0
  144. package/dist/space-sdk/src/hooks/useHandover.d.ts +43 -0
  145. package/dist/space-sdk/src/hooks/useLongPressSelection.d.ts +9 -0
  146. package/dist/space-sdk/src/hooks/useMarqueeSelection.d.ts +57 -0
  147. package/dist/space-sdk/src/hooks/useMultiSelect.d.ts +51 -0
  148. package/dist/space-sdk/src/hooks/useNativeDocumentBody.d.ts +24 -0
  149. package/dist/space-sdk/src/hooks/useNativeDocuments.d.ts +55 -0
  150. package/dist/space-sdk/src/hooks/useNotis.d.ts +25 -0
  151. package/dist/space-sdk/src/hooks/useNotisNavigation.d.ts +33 -0
  152. package/dist/space-sdk/src/hooks/useQuery.d.ts +22 -0
  153. package/dist/space-sdk/src/hooks/useTool.d.ts +20 -0
  154. package/dist/space-sdk/src/hooks/useToolQuery.d.ts +3 -0
  155. package/dist/space-sdk/src/hooks/useTools.d.ts +15 -0
  156. package/dist/space-sdk/src/hooks/useTopBarSearch.d.ts +48 -0
  157. package/dist/space-sdk/src/hooks/useTopBarTitleLeaf.d.ts +24 -0
  158. package/dist/space-sdk/src/hooks/useUpsertDocument.d.ts +27 -0
  159. package/dist/space-sdk/src/hooks/useViewParams.d.ts +76 -0
  160. package/dist/space-sdk/src/hooks/useViewerReads.d.ts +38 -0
  161. package/dist/space-sdk/src/index.d.ts +98 -0
  162. package/dist/space-sdk/src/interactions/actions.d.ts +33 -0
  163. package/dist/space-sdk/src/interactions/shortcuts.d.ts +42 -0
  164. package/dist/space-sdk/src/interactions/visibility.d.ts +2 -0
  165. package/dist/space-sdk/src/nativeData.d.ts +66 -0
  166. package/dist/space-sdk/src/provider.d.ts +17 -0
  167. package/dist/space-sdk/src/queryCache.d.ts +45 -0
  168. package/dist/space-sdk/src/queryErrors.ts +51 -0
  169. package/dist/space-sdk/src/runtime.d.ts +615 -0
  170. package/dist/space-sdk/src/runtimeModules.ts +33 -0
  171. package/dist/space-sdk/src/space.ts +442 -0
  172. package/dist/space-sdk/src/standardViewParams.d.ts +15 -0
  173. package/dist/space-sdk/src/tailwind-candidates.d.ts +3418 -0
  174. package/dist/space-sdk/src/tailwind.ts +66 -0
  175. package/dist/space-sdk/src/ui/actions/ActionToast.d.ts +15 -0
  176. package/dist/space-sdk/src/ui/actions/actionWords.d.ts +16 -0
  177. package/dist/space-sdk/src/ui/actions/index.d.ts +7 -0
  178. package/dist/space-sdk/src/ui/actions/useActions.d.ts +69 -0
  179. package/dist/space-sdk/src/ui/actions/useToast.d.ts +41 -0
  180. package/dist/space-sdk/src/ui/blocks/Tabs.d.ts +139 -0
  181. package/dist/space-sdk/src/ui/blocks/blocks.d.ts +217 -0
  182. package/dist/space-sdk/src/ui/blocks/index.d.ts +4 -0
  183. package/dist/space-sdk/src/ui/board/BoardView.d.ts +36 -0
  184. package/dist/space-sdk/src/ui/board/boardModel.d.ts +46 -0
  185. package/dist/space-sdk/src/ui/board/boardWords.d.ts +9 -0
  186. package/dist/space-sdk/src/ui/board/index.d.ts +5 -0
  187. package/dist/space-sdk/src/ui/calendar/CalendarView.d.ts +49 -0
  188. package/dist/space-sdk/src/ui/calendar/DayPicker.d.ts +23 -0
  189. package/dist/space-sdk/src/ui/calendar/calendarMath.d.ts +81 -0
  190. package/dist/space-sdk/src/ui/charts/Chart.d.ts +203 -0
  191. package/dist/space-sdk/src/ui/charts/ChartTable.d.ts +37 -0
  192. package/dist/space-sdk/src/ui/charts/RenderChartData.d.ts +16 -0
  193. package/dist/space-sdk/src/ui/charts/StatRow.d.ts +59 -0
  194. package/dist/space-sdk/src/ui/charts/chartData.d.ts +84 -0
  195. package/dist/space-sdk/src/ui/comments/CommentPane.d.ts +58 -0
  196. package/dist/space-sdk/src/ui/comments/commentModel.d.ts +157 -0
  197. package/dist/space-sdk/src/ui/comments/commentWords.d.ts +60 -0
  198. package/dist/space-sdk/src/ui/comments/index.d.ts +4 -0
  199. package/dist/space-sdk/src/ui/comments/useComments.d.ts +146 -0
  200. package/dist/space-sdk/src/ui/engine/HelpSheet.d.ts +12 -0
  201. package/dist/space-sdk/src/ui/engine/KeyboardEngine.d.ts +27 -0
  202. package/dist/space-sdk/src/ui/engine/core.d.ts +73 -0
  203. package/dist/space-sdk/src/ui/engine/hooks.d.ts +64 -0
  204. package/dist/space-sdk/src/ui/engine/index.d.ts +14 -0
  205. package/dist/space-sdk/src/ui/engine/instance.d.ts +21 -0
  206. package/dist/space-sdk/src/ui/engine/keycapContext.d.ts +2 -0
  207. package/dist/space-sdk/src/ui/engine/registry.d.ts +77 -0
  208. package/dist/space-sdk/src/ui/engine/step.d.ts +11 -0
  209. package/dist/space-sdk/src/ui/engine/types.d.ts +54 -0
  210. package/dist/space-sdk/src/ui/fields/fieldWords.d.ts +56 -0
  211. package/dist/space-sdk/src/ui/fields/fields.d.ts +162 -0
  212. package/dist/space-sdk/src/ui/fields/index.d.ts +5 -0
  213. package/dist/space-sdk/src/ui/fields/useFieldDraft.d.ts +22 -0
  214. package/dist/space-sdk/src/ui/gallery/AttachmentGallery.d.ts +53 -0
  215. package/dist/space-sdk/src/ui/gallery/ImageViewer.d.ts +27 -0
  216. package/dist/space-sdk/src/ui/gallery/galleryWords.d.ts +95 -0
  217. package/dist/space-sdk/src/ui/gallery/index.d.ts +5 -0
  218. package/dist/space-sdk/src/ui/icons.d.ts +13 -0
  219. package/dist/space-sdk/src/ui/index.d.ts +54 -0
  220. package/dist/space-sdk/src/ui/keycap/Keycap.d.ts +22 -0
  221. package/dist/space-sdk/src/ui/keycap/index.d.ts +2 -0
  222. package/dist/space-sdk/src/ui/layout/Panels.d.ts +50 -0
  223. package/dist/space-sdk/src/ui/layout/chatPanelEdge.d.ts +77 -0
  224. package/dist/space-sdk/src/ui/layout/index.d.ts +5 -0
  225. package/dist/space-sdk/src/ui/layout/panelMath.d.ts +91 -0
  226. package/dist/space-sdk/src/ui/list/ListReader.d.ts +380 -0
  227. package/dist/space-sdk/src/ui/list/Row.d.ts +145 -0
  228. package/dist/space-sdk/src/ui/list/SelectionBar.d.ts +65 -0
  229. package/dist/space-sdk/src/ui/list/index.d.ts +14 -0
  230. package/dist/space-sdk/src/ui/list/rowState.d.ts +22 -0
  231. package/dist/space-sdk/src/ui/list/useListKeys.d.ts +89 -0
  232. package/dist/space-sdk/src/ui/list/useReorder.d.ts +86 -0
  233. package/dist/space-sdk/src/ui/list/useSelection.d.ts +90 -0
  234. package/dist/space-sdk/src/ui/open/OpenAsMenu.d.ts +15 -0
  235. package/dist/space-sdk/src/ui/open/index.d.ts +6 -0
  236. package/dist/space-sdk/src/ui/open/openModeStore.d.ts +39 -0
  237. package/dist/space-sdk/src/ui/open/useOpenMode.d.ts +17 -0
  238. package/dist/space-sdk/src/ui/overlay/Menu.d.ts +88 -0
  239. package/dist/space-sdk/src/ui/overlay/Popover.d.ts +28 -0
  240. package/dist/space-sdk/src/ui/overlay/Tooltip.d.ts +19 -0
  241. package/dist/space-sdk/src/ui/overlay/index.d.ts +9 -0
  242. package/dist/space-sdk/src/ui/overlay/layerRoot.d.ts +24 -0
  243. package/dist/space-sdk/src/ui/overlay/position.d.ts +31 -0
  244. package/dist/space-sdk/src/ui/period/PeriodPicker.d.ts +30 -0
  245. package/dist/space-sdk/src/ui/period/dateFormat.d.ts +50 -0
  246. package/dist/space-sdk/src/ui/period/dateWords.d.ts +88 -0
  247. package/dist/space-sdk/src/ui/period/periods.d.ts +70 -0
  248. package/dist/space-sdk/src/ui/period/usePeriod.d.ts +23 -0
  249. package/dist/space-sdk/src/ui/present/index.d.ts +3 -0
  250. package/dist/space-sdk/src/ui/present/presentWords.d.ts +27 -0
  251. package/dist/space-sdk/src/ui/present/screens.d.ts +36 -0
  252. package/dist/space-sdk/src/ui/present/useAudienceScreen.d.ts +38 -0
  253. package/dist/space-sdk/src/ui/styles.d.ts +21 -0
  254. package/dist/space-sdk/src/ui/timeline/TimelineView.d.ts +44 -0
  255. package/dist/space-sdk/src/ui/timeline/timelineMath.d.ts +123 -0
  256. package/dist/space-sdk/src/viewPeriod.d.ts +8 -0
  257. package/dist/space-sdk/src/vite.ts +92 -0
  258. package/dist/space-sdk/src/words/actions.ts +295 -0
  259. package/dist/space-sdk/src/words/clashes.ts +158 -0
  260. package/dist/space-sdk/src/words/format.ts +136 -0
  261. package/dist/space-sdk/src/words/index.ts +13 -0
  262. package/dist/space-sdk/src/words/keymap.ts +72 -0
  263. package/dist/space-sdk/src/words/keys.ts +297 -0
  264. package/dist/space-sdk/src/words/shell.ts +46 -0
  265. package/dist/space-sdk/src/words/types.ts +65 -0
  266. package/dist/view-renderer/dist/host.css +1 -0
  267. package/dist/view-renderer/dist/host.js +2374 -0
  268. package/dist/view-renderer/dist/theme.css +73 -0
  269. package/dist/view-renderer/src/dom.js +212 -0
  270. package/dist/view-renderer/src/errors.js +8 -0
  271. package/dist/view-renderer/src/host.tsx +250 -0
  272. package/dist/view-renderer/src/index.js +4 -0
  273. package/dist/view-renderer/src/png.js +213 -0
  274. package/dist/view-renderer/src/pool.js +110 -0
  275. package/dist/view-renderer/src/remote-broker.js +59 -0
  276. package/dist/view-renderer/src/renderer.js +279 -0
  277. package/package.json +21 -11
  278. package/skills/notis-cli/AGENT_INSTRUCTIONS.md +63 -12
  279. package/skills/notis-onboarding/BRIEF.md +14 -9
  280. package/skills/notis-query/cli.md +1 -1
  281. package/skills/notis-spaces/cli.md +540 -0
  282. package/src/cli.js +8 -8
  283. package/src/command-specs/agents.js +121 -8
  284. package/src/command-specs/auth.js +3 -3
  285. package/src/command-specs/diagnostics.js +97 -20
  286. package/src/command-specs/documents.js +158 -0
  287. package/src/command-specs/files.js +29 -0
  288. package/src/command-specs/handover.js +331 -79
  289. package/src/command-specs/helpers.js +50 -40
  290. package/src/command-specs/index.js +14 -5
  291. package/src/command-specs/meta.js +38 -9
  292. package/src/command-specs/onboarding.js +1 -1
  293. package/src/command-specs/profile.js +42 -1
  294. package/src/command-specs/skills.js +314 -9
  295. package/src/command-specs/smoke.js +16 -4
  296. package/src/command-specs/spaces.js +783 -0
  297. package/src/command-specs/tools.js +118 -21
  298. package/src/command-specs/version-history.js +20 -0
  299. package/src/command-specs/views.js +69 -0
  300. package/src/runtime/agent-browser.js +178 -267
  301. package/src/runtime/agent-setup.js +172 -13
  302. package/src/runtime/app-boundary-validator.js +338 -2
  303. package/src/runtime/app-data-literals.js +82 -0
  304. package/src/runtime/app-platform.js +750 -1570
  305. package/src/runtime/auth-recovery.js +17 -0
  306. package/src/runtime/base-skills.d.ts +11 -0
  307. package/src/runtime/base-skills.js +100 -3
  308. package/src/runtime/computer-identity.d.ts +30 -0
  309. package/src/runtime/computer-identity.js +151 -0
  310. package/src/runtime/config-lock.js +135 -0
  311. package/src/runtime/confined-space-build.js +74 -0
  312. package/src/runtime/confined-space-verify.js +49 -0
  313. package/src/runtime/delegated-context.js +5 -21
  314. package/src/runtime/native-record-files.js +47 -0
  315. package/src/runtime/oauth.js +342 -31
  316. package/src/runtime/profiles.js +19 -112
  317. package/src/runtime/retired-sdk-files.js +24 -0
  318. package/src/runtime/retry.js +150 -0
  319. package/src/runtime/skill-sync/cloud-client.ts +33 -8
  320. package/src/runtime/skill-sync/index.ts +443 -106
  321. package/src/runtime/skill-sync/local-scanner.ts +248 -32
  322. package/src/runtime/skill-sync/native-identity.ts +102 -0
  323. package/src/runtime/skill-sync/native-plan.ts +70 -0
  324. package/src/runtime/skill-sync/symlink-manager.ts +66 -16
  325. package/src/runtime/skill-sync/sync-plan.ts +34 -2
  326. package/src/runtime/skill-sync/types.ts +54 -0
  327. package/src/runtime/skill-sync/write-cloud-skill.ts +4 -4
  328. package/src/runtime/skill-sync-service.js +109 -0
  329. package/src/runtime/source-root.js +32 -0
  330. package/src/runtime/space-collection-scaffold.js +107 -0
  331. package/src/runtime/space-database-structures.js +229 -0
  332. package/src/runtime/space-fixture-broker.js +68 -0
  333. package/src/runtime/space-lock.js +337 -0
  334. package/src/runtime/space-platform.js +133 -0
  335. package/src/runtime/space-source.js +290 -0
  336. package/src/runtime/space-test-server.js +354 -0
  337. package/src/runtime/space-verification.js +94 -0
  338. package/src/runtime/space-verify-browser-worker.js +51 -0
  339. package/src/runtime/space-view-manifest.js +368 -0
  340. package/src/runtime/sync-skills.d.ts +5 -1
  341. package/src/runtime/sync-skills.js +11 -2
  342. package/src/runtime/tool-confirmation.js +138 -0
  343. package/src/runtime/transport.js +118 -24
  344. package/src/runtime/upsell.js +219 -0
  345. package/src/runtime/view-render.js +49 -0
  346. package/src/skill-sync-worker-entry.js +2 -0
  347. package/src/skill-sync-worker.js +114 -0
  348. package/src/space-harness-host.ts +122 -0
  349. package/template/.harness/index.html.tmpl +9 -352
  350. package/template/app/globals.css +29 -3
  351. package/template/app/layout.tsx +2 -6
  352. package/template/components/ui/badge.tsx +7 -4
  353. package/template/components/ui/button.tsx +1 -1
  354. package/template/components/ui/native-select.tsx +24 -0
  355. package/template/notis.config.ts +4 -36
  356. package/template/package-lock.json +1800 -1867
  357. package/template/package.json +16 -14
  358. package/template/postcss.config.mjs +1 -1
  359. package/template/tailwind.config.ts +1 -6
  360. package/template/tsconfig.json +1 -0
  361. package/template/vite.config.ts +2 -2
  362. package/dist/base-skills/notis-apps/SKILL.md +0 -613
  363. package/skills/notis-apps/cli.md +0 -285
  364. package/src/command-specs/apps.js +0 -3011
  365. package/src/runtime/app-changelog.js +0 -79
  366. package/src/runtime/app-dev-consumers.js +0 -154
  367. package/src/runtime/app-dev-host-lock.js +0 -80
  368. package/src/runtime/app-dev-process-identity.js +0 -111
  369. package/src/runtime/app-dev-roots.js +0 -284
  370. package/src/runtime/app-dev-server.js +0 -1140
  371. package/src/runtime/app-dev-sessions.js +0 -185
  372. package/src/runtime/app-registry-scaffolds.js +0 -367
  373. package/src/runtime/assets/store-screenshot-dark.png +0 -0
  374. package/src/runtime/cli-mode.generated.js +0 -5
  375. package/src/runtime/cli-mode.js +0 -34
  376. package/src/runtime/store-screenshot.js +0 -143
  377. package/template/CHANGELOG.md +0 -5
  378. package/template/app/page.tsx +0 -60
  379. package/template/components/ui/card.tsx +0 -56
  380. package/template/packages/sdk/src/components/DocumentEditor.tsx +0 -103
  381. package/template/packages/sdk/src/config.ts +0 -259
  382. package/template/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  383. package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  384. package/template/packages/sdk/src/hooks/useDocument.ts +0 -78
  385. package/template/packages/sdk/src/hooks/useNotisNavigation.ts +0 -52
  386. package/template/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  387. package/template/packages/sdk/src/runtime.ts +0 -439
  388. package/template/packages/sdk/src/vite.ts +0 -56
  389. package/template/packages/sdk/tsconfig.json +0 -15
  390. /package/{template/packages → dist}/sdk/src/components/MultiSelectDragOverlay.tsx +0 -0
  391. /package/{template/packages → dist}/sdk/src/components/ShortcutHints.tsx +0 -0
  392. /package/{template/packages → dist}/sdk/src/hooks/useActiveResource.ts +0 -0
  393. /package/{template/packages → dist}/sdk/src/hooks/useTool.ts +0 -0
  394. /package/{template/packages → dist}/sdk/src/hooks/useTools.ts +0 -0
  395. /package/{template/packages → dist}/sdk/src/ui.ts +0 -0
@@ -1,18 +1,17 @@
1
- import { randomUUID } from 'node:crypto';
2
1
  import {
3
2
  existsSync,
4
3
  mkdirSync,
5
4
  readFileSync,
6
5
  renameSync,
7
- rmSync,
8
- statSync,
9
6
  writeFileSync,
10
7
  } from 'node:fs';
11
8
  import { homedir } from 'node:os';
12
9
  import { dirname, join, parse, resolve } from 'node:path';
13
10
  import { CliError, EXIT_CODES } from './errors.js';
11
+ import { withConfigFileWriteLock } from './config-lock.js';
14
12
  import { getAuthRecovery, quoteShellArgument } from './auth-recovery.js';
15
13
  import { channelFromProfile, cliCommandForChannel, isReleaseChannel } from './channel.js';
14
+ import { resolveRetryEnabled } from './retry.js';
16
15
 
17
16
  export const CONFIG_DIR = join(homedir(), '.notis');
18
17
  export const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
@@ -34,14 +33,6 @@ const WORKTREE_RUNTIME_FILENAME = join('.context', 'notis-runtime.json');
34
33
  const WORKTREE_ROUTING_FILENAME = join('.context', 'notis-routing.json');
35
34
  const LOCAL_API_HOSTS = new Set(['localhost', '127.0.0.1', '::1']);
36
35
  const LIVE_API_HOSTS = new Set(['api.notis.ai', 'api-beta.notis.ai']);
37
- // Cross-process write lock over ~/.notis/config.json. Independent `notis`
38
- // processes (a `login` in one terminal and a `tools exec` in another) all
39
- // rewrite this file, so the lock directory name and these three timings are the
40
- // whole coordination protocol. Guarded by the concurrency test in
41
- // packages/cli/test/runtime-auth.test.js.
42
- const CONFIG_WRITE_LOCK_TIMEOUT_MS = 5_000;
43
- const CONFIG_WRITE_LOCK_STALE_MS = 2_000;
44
- const CONFIG_WRITE_LOCK_POLL_MS = 10;
45
36
 
46
37
  function clone(value) {
47
38
  return JSON.parse(JSON.stringify(value));
@@ -206,7 +197,7 @@ function readJsonFile(path) {
206
197
  }
207
198
  }
208
199
 
209
- function findUp(filename, startDir = process.cwd()) {
200
+ function findUp(filename, startDir = process.cwd(), boundary = null) {
210
201
  let current = resolve(startDir);
211
202
  const root = parse(current).root;
212
203
  while (true) {
@@ -214,7 +205,7 @@ function findUp(filename, startDir = process.cwd()) {
214
205
  if (existsSync(candidate)) {
215
206
  return candidate;
216
207
  }
217
- if (current === root) {
208
+ if (current === root || current === boundary) {
218
209
  return null;
219
210
  }
220
211
  current = dirname(current);
@@ -228,8 +219,11 @@ function processIsAlive(pid) {
228
219
  try {
229
220
  process.kill(pid, 0);
230
221
  return true;
231
- } catch {
232
- return false;
222
+ } catch (error) {
223
+ // Sandboxed children may be allowed to observe the worktree lease but not
224
+ // signal its dev.sh supervisor. POSIX EPERM proves that the PID exists;
225
+ // ESRCH (and every other probe failure) does not.
226
+ return error?.code === 'EPERM';
233
227
  }
234
228
  }
235
229
 
@@ -240,8 +234,11 @@ export function resolveWorktreeRuntime(startDir = process.cwd()) {
240
234
  ) {
241
235
  return null;
242
236
  }
243
- const runtimePath = findUp(WORKTREE_RUNTIME_FILENAME, startDir);
244
237
  const routingPath = findUp(WORKTREE_ROUTING_FILENAME, startDir);
238
+ // A nested worktree's persistent policy survives its lease. Never cross that
239
+ // boundary and borrow a still-running ancestor worktree's credential/route.
240
+ const boundary = routingPath ? dirname(dirname(routingPath)) : null;
241
+ const runtimePath = findUp(WORKTREE_RUNTIME_FILENAME, startDir, boundary);
245
242
  const routing = routingPath ? readJsonFile(routingPath) : null;
246
243
 
247
244
  if (!runtimePath) {
@@ -369,103 +366,11 @@ function writeRawConfig(configFile, config) {
369
366
  renameSync(temporaryFile, configFile);
370
367
  }
371
368
 
372
- // The lock records who holds it and when they took it. Reclaiming a stale lock
373
- // by mtime alone is not safe: the reclaimer cannot tell an abandoned lock from
374
- // one it is about to steal from a live writer, and the victim's own release
375
- // would then delete the thief's lock and admit a third writer. Both sides are
376
- // on the same host, so the stored timestamp and Date.now() share a clock.
377
- function readLockOwner(lockDirectory) {
378
- try {
379
- return JSON.parse(readFileSync(join(lockDirectory, 'owner'), 'utf-8'));
380
- } catch {
381
- return null;
382
- }
383
- }
384
-
385
- function lockDirectoryMtime(lockDirectory) {
386
- try {
387
- return statSync(lockDirectory).mtimeMs;
388
- } catch {
389
- return null;
390
- }
391
- }
392
-
393
- function publishConfigWriteLock(lockDirectory, ownerId) {
394
- const candidateDirectory = `${lockDirectory}.candidate-${ownerId}`;
395
- try {
396
- mkdirSync(candidateDirectory);
397
- writeFileSync(
398
- join(candidateDirectory, 'owner'),
399
- JSON.stringify({ id: ownerId, at: Date.now() }),
400
- { mode: 0o600 },
401
- );
402
- try {
403
- renameSync(candidateDirectory, lockDirectory);
404
- return true;
405
- } catch (error) {
406
- if (!existsSync(lockDirectory)) throw error;
407
- return false;
408
- }
409
- } finally {
410
- rmSync(candidateDirectory, { recursive: true, force: true });
411
- }
412
- }
413
-
369
+ // Independent `notis` processes and Desktop all rewrite this file. The lock
370
+ // protocol lives in config-lock.js, shared with computer.json and mirrored by
371
+ // Desktop.
414
372
  function withConfigWriteLock(callback) {
415
- const configFile = resolveConfigFile();
416
- const lockDirectory = `${configFile}.write-lock`;
417
- const ownerId = `${process.pid}.${randomUUID()}`;
418
- const deadline = Date.now() + CONFIG_WRITE_LOCK_TIMEOUT_MS;
419
- mkdirSync(dirname(configFile), { recursive: true });
420
- for (;;) {
421
- if (!existsSync(lockDirectory) && publishConfigWriteLock(lockDirectory, ownerId)) {
422
- break;
423
- }
424
-
425
- const owner = readLockOwner(lockDirectory);
426
- const observedMtime = lockDirectoryMtime(lockDirectory);
427
- const ownerIsStale =
428
- owner?.id && Date.now() - Number(owner.at) > CONFIG_WRITE_LOCK_STALE_MS;
429
- const ownerlessIsStale =
430
- !owner
431
- && observedMtime !== null
432
- && Date.now() - observedMtime > CONFIG_WRITE_LOCK_STALE_MS;
433
- if (ownerIsStale || ownerlessIsStale) {
434
- try {
435
- const currentOwner = readLockOwner(lockDirectory);
436
- const ownerUnchanged = owner?.id
437
- ? currentOwner?.id === owner.id
438
- : !currentOwner && lockDirectoryMtime(lockDirectory) === observedMtime;
439
- if (ownerUnchanged) {
440
- rmSync(lockDirectory, { recursive: true, force: true });
441
- continue;
442
- }
443
- } catch {
444
- // Losing the reclaim race is normal; fall through and keep waiting.
445
- }
446
- }
447
- if (Date.now() >= deadline) {
448
- throw new Error(`Timed out waiting to update ${configFile}`);
449
- }
450
- Atomics.wait(
451
- new Int32Array(new SharedArrayBuffer(4)),
452
- 0,
453
- 0,
454
- CONFIG_WRITE_LOCK_POLL_MS,
455
- );
456
- }
457
- try {
458
- return callback(configFile);
459
- } finally {
460
- try {
461
- // Never release a lock that was reclaimed from us while we worked.
462
- if (readLockOwner(lockDirectory)?.id === ownerId) {
463
- rmSync(lockDirectory, { recursive: true, force: true });
464
- }
465
- } catch {
466
- // The lock may already have been removed after an interrupted write.
467
- }
468
- }
373
+ return withConfigFileWriteLock(resolveConfigFile(), callback);
469
374
  }
470
375
 
471
376
  export function saveConfig(config) {
@@ -967,6 +872,8 @@ export function resolveRuntimeProfile(
967
872
  nonInteractive,
968
873
  outputMode,
969
874
  timeoutMs,
875
+ // Automatic retries of interrupted calls that are safe to repeat.
876
+ retry: resolveRetryEnabled(globalOptions),
970
877
  debugEntitlementOverride,
971
878
  worktreeRuntime: devRuntime,
972
879
  detachedWorktreeRuntime: devRuntime ? null : worktreeRuntime,
@@ -0,0 +1,24 @@
1
+ /** Exact canonical files retired from the embedded SDK (one-Shadow-runtime cutover, toolkit move).
2
+ * Modified or additional source is retained for explicit reconciliation. */
3
+ // Host mounting code stays in the canonical SDK and CLI host bundle, not in
4
+ // authored apps' public SDK mirror. It is not a public package export.
5
+ export const HOST_ONLY_SDK_FILES = Object.freeze(['src/presentation.tsx']);
6
+
7
+ export const RETIRED_SDK_FILES = Object.freeze({
8
+ "src/presentation.tsx": [
9
+ "d29022f92e1e96b451b62cc8a561878a1c0cf8a3b4c0419181de7266f8a1b525"
10
+ ],
11
+ "src/isolatedHost.tsx": [
12
+ "7f59049281a9533d39c557ca75635073c2f404f1eb0a528b274323d61db776cd"
13
+ ],
14
+ "src/frameProtocol.ts": [
15
+ "f32337ddb507718be04289a4acf50cf2485698bc716556230f35f06a6f633f19"
16
+ ],
17
+ "src/frameIsolation.ts": [
18
+ "8b57cb38f618eefc4133ae50fbf2b37bbdef5be02e44b39758d8328fb2a59ab6"
19
+ ],
20
+ // Moved to src/ui/charts/RenderChartData.tsx with the Spaces toolkit.
21
+ "src/components/RenderChartData.tsx": [
22
+ "a3a8ab3ffe4cd7807e0591b6dace77c24dd08cb556e4170595783e986a513808"
23
+ ]
24
+ });
@@ -0,0 +1,150 @@
1
+ import { CliError } from './errors.js';
2
+
3
+ /**
4
+ * Safe automatic retries for tool calls.
5
+ *
6
+ * A call is replayed only when the replay is provably harmless:
7
+ * - A read the server proves read-only. Keyless writes are refused before
8
+ * they run, so replaying a keyless call never repeats an effect.
9
+ * - A call with an idempotency key. The server stores the first outcome under
10
+ * that key, so the same key and the same body either join the running call,
11
+ * or return its stored answer.
12
+ * - A native write that answered "outcome unknown" and carries its own
13
+ * `request_id`. Its stored answer is that unknown outcome, so the replay
14
+ * sends identical arguments under a fresh idempotency key and the native
15
+ * receipt for the `request_id` decides whether it already happened.
16
+ *
17
+ * Every other failure is returned after the first attempt.
18
+ */
19
+
20
+ export const RETRY_MAX_ATTEMPTS = 3;
21
+ export const RETRY_BASE_DELAY_MS = 250;
22
+ export const RETRY_MAX_DELAY_MS = 2000;
23
+ const RETRY_MIN_ATTEMPT_WINDOW_MS = 1000;
24
+
25
+ const TRANSIENT_HTTP_STATUSES = new Set([502, 503, 504]);
26
+ const TRANSIENT_NETWORK_CODES = new Set(['network_error', 'network_timeout']);
27
+
28
+ // Native tool results that end with an unknown outcome and dedupe a replay by
29
+ // the caller's `request_id`.
30
+ export const TOOL_OUTCOME_UNKNOWN_CODES = new Set([
31
+ 'document_outcome_unknown',
32
+ 'record_file_outcome_unknown',
33
+ ]);
34
+
35
+ const FALSE_VALUES = new Set(['', '0', 'false', 'no', 'off']);
36
+
37
+ /** `--no-retry` or a truthy NOTIS_NO_RETRY turns automatic retries off. */
38
+ export function resolveRetryEnabled(globalOptions = {}, env = process.env) {
39
+ if (globalOptions.retry === false) return false;
40
+ const raw = env.NOTIS_NO_RETRY;
41
+ if (raw === undefined || raw === null) return true;
42
+ return FALSE_VALUES.has(String(raw).trim().toLowerCase());
43
+ }
44
+
45
+ export function retriesEnabled(runtime) {
46
+ return runtime?.retry !== false;
47
+ }
48
+
49
+ function backendErrorCode(details) {
50
+ const error = details?.error;
51
+ if (error && typeof error === 'object' && typeof error.code === 'string') return error.code;
52
+ return typeof error === 'string' ? error : null;
53
+ }
54
+
55
+ /** Whether the failure is temporary, so the same request may succeed. */
56
+ export function isTransientFailure(error) {
57
+ if (!(error instanceof CliError)) return false;
58
+ if (TRANSIENT_NETWORK_CODES.has(error.code)) return true;
59
+ const code = backendErrorCode(error.details);
60
+ // An unknown outcome is a final answer for this key, never a transport blip.
61
+ if (typeof code === 'string' && code.endsWith('_outcome_unknown')) return false;
62
+ if (TRANSIENT_HTTP_STATUSES.has(error.httpStatus)) return true;
63
+ return error.httpStatus === 409 && code === 'idempotency_in_progress';
64
+ }
65
+
66
+ /** Whether sending the identical request again can never repeat an effect. */
67
+ export function canReplayRequest({ mutating, idempotencyKey }) {
68
+ return Boolean(idempotencyKey) || !mutating;
69
+ }
70
+
71
+ /**
72
+ * The native outcome-unknown code a tool answer carries, if any. The answer
73
+ * may be the tool output itself or a multi-execute envelope around it.
74
+ */
75
+ export function findToolOutcomeUnknown(payload, depth = 0) {
76
+ if (!payload || typeof payload !== 'object' || depth > 6) return null;
77
+ if (Array.isArray(payload)) {
78
+ for (const item of payload) {
79
+ const code = findToolOutcomeUnknown(item, depth + 1);
80
+ if (code) return code;
81
+ }
82
+ return null;
83
+ }
84
+ const own = backendErrorCode(payload);
85
+ if (own && TOOL_OUTCOME_UNKNOWN_CODES.has(own)) return own;
86
+ for (const value of Object.values(payload)) {
87
+ if (value && typeof value === 'object') {
88
+ const code = findToolOutcomeUnknown(value, depth + 1);
89
+ if (code) return code;
90
+ }
91
+ }
92
+ return null;
93
+ }
94
+
95
+ function hasRequestId(args) {
96
+ return Boolean(args) && typeof args === 'object' && typeof args.request_id === 'string'
97
+ && args.request_id.trim().length > 0;
98
+ }
99
+
100
+ /**
101
+ * Whether the call names exactly one tool whose arguments carry a stable
102
+ * `request_id`. A batch of several tools is never resent: its other items
103
+ * might not deduplicate a second run.
104
+ */
105
+ export function callCarriesToolRequestId(argumentsPayload) {
106
+ if (!argumentsPayload || typeof argumentsPayload !== 'object') return false;
107
+ if (Array.isArray(argumentsPayload.tools)) {
108
+ return argumentsPayload.tools.length === 1 && hasRequestId(argumentsPayload.tools[0]?.arguments);
109
+ }
110
+ return hasRequestId(argumentsPayload);
111
+ }
112
+
113
+ export function retryDelayMs(attempt, random = Math.random) {
114
+ const ceiling = Math.min(RETRY_MAX_DELAY_MS, RETRY_BASE_DELAY_MS * (2 ** (attempt - 1)));
115
+ return Math.round(ceiling / 2 + random() * (ceiling / 2));
116
+ }
117
+
118
+ /**
119
+ * Wait before the next attempt when it still fits in the overall time budget.
120
+ * Returns false when no attempt is left or the budget is spent.
121
+ */
122
+ export async function waitForNextAttempt({ attempt, deadline, budgetMs, now = Date.now, sleep = defaultSleep }) {
123
+ if (attempt >= RETRY_MAX_ATTEMPTS) return false;
124
+ const delay = retryDelayMs(attempt);
125
+ const window = Math.min(RETRY_MIN_ATTEMPT_WINDOW_MS, Math.floor(budgetMs / 4));
126
+ if (deadline - now() - delay < window) return false;
127
+ await sleep(delay);
128
+ return true;
129
+ }
130
+
131
+ function defaultSleep(ms) {
132
+ return new Promise((resolve) => { setTimeout(resolve, ms); });
133
+ }
134
+
135
+ /** Record how many attempts a failed call made, without changing its shape. */
136
+ export function withAttempts(error, attempts) {
137
+ if (error instanceof CliError) {
138
+ const details = error.details && typeof error.details === 'object' && !Array.isArray(error.details)
139
+ ? error.details
140
+ : {};
141
+ error.details = { ...details, attempts };
142
+ if (attempts > 1) {
143
+ error.hints = [
144
+ ...(Array.isArray(error.hints) ? error.hints : []),
145
+ { reason: `The CLI already tried this call ${attempts} times. Check the cause before running it again.` },
146
+ ];
147
+ }
148
+ }
149
+ return error;
150
+ }
@@ -1,5 +1,7 @@
1
- import { createSkillBundleBase64 } from './local-scanner';
2
- import type { AgentTargets, LocalSkill, SyncPullResponse, SyncPushResponse, SyncSettings } from './types';
1
+ import { captureNativeSkillFiles, createSkillBundleBase64 } from './local-scanner';
2
+ import { createHash } from 'node:crypto';
3
+ import { checkedNativeReference } from './native-identity';
4
+ import type { AgentTargets, LocalSkill, NativeSkillReference, SyncPullResponse, SyncPushResponse, SyncSettings } from './types';
3
5
 
4
6
  type JsonBody = Record<string, unknown> | undefined;
5
7
 
@@ -9,6 +11,7 @@ async function requestJson<T>(
9
11
  options: { method?: string; body?: JsonBody } = {},
10
12
  ): Promise<T> {
11
13
  const response = await fetch(url, {
14
+ signal: AbortSignal.timeout(90_000),
12
15
  method: options.method || 'POST',
13
16
  headers: {
14
17
  'Content-Type': 'application/json',
@@ -33,7 +36,7 @@ export async function fetchSyncSettings(serverUrl: string, jwt: string): Promise
33
36
 
34
37
  export async function pullSkills(serverUrl: string, jwt: string): Promise<SyncPullResponse> {
35
38
  return requestJson<SyncPullResponse>(`${serverUrl}/portal_skills/sync-pull`, jwt, {
36
- body: {},
39
+ body: { skill_sync_protocol: 2 },
37
40
  });
38
41
  }
39
42
 
@@ -48,18 +51,23 @@ export async function pushChangedSkills(
48
51
  skill_md: skill.skillMd,
49
52
  source_url: skill.sourceUrl,
50
53
  folder_hash: skill.folderHash,
51
- bundle_base64: await createSkillBundleBase64(skill),
54
+ ...(skill.nativeReference ? { bundle_files: await captureNativeSkillFiles(skill) }
55
+ : { bundle_base64: await createSkillBundleBase64(skill) }),
56
+ ...(skill.cloudId ? { cloud_id: skill.cloudId } : {}),
57
+ ...(skill.nativeReference ? { native_reference: skill.nativeReference, display_name: skill.displayName,
58
+ native_scope: skill.nativeScope } : {}),
52
59
  })));
53
60
 
54
61
  return requestJson<SyncPushResponse>(`${serverUrl}/portal_skills/sync-push`, jwt, {
55
62
  body: {
63
+ skill_sync_protocol: 2,
56
64
  skills: payloadSkills,
57
65
  },
58
66
  });
59
67
  }
60
68
 
61
69
  export async function downloadSkillBundle(bundleUrl: string): Promise<Buffer> {
62
- const response = await fetch(bundleUrl);
70
+ const response = await fetch(bundleUrl, { signal: AbortSignal.timeout(90_000) });
63
71
  if (!response.ok) {
64
72
  const text = await response.text();
65
73
  throw new Error(`GET ${bundleUrl} → ${response.status}: ${text}`);
@@ -72,13 +80,30 @@ export async function updateAgentTargets(
72
80
  serverUrl: string,
73
81
  jwt: string,
74
82
  skillId: string,
75
- targets: AgentTargets,
76
- ): Promise<{ success: boolean; agent_targets: AgentTargets }> {
77
- return requestJson<{ success: boolean; agent_targets: AgentTargets }>(`${serverUrl}/portal_skills/agent-targets`, jwt, {
83
+ targets: Partial<AgentTargets>,
84
+ expectedUpdatedAt?: string,
85
+ native?: { reference: NativeSkillReference; settingsRevision: number },
86
+ ): Promise<{ success: boolean; agent_targets: AgentTargets; updated_at?: string }> {
87
+ if (native) {
88
+ checkedNativeReference(native.reference, skillId);
89
+ if (!Number.isSafeInteger(native.settingsRevision) || native.settingsRevision < 0) throw new Error('Refresh native Skill settings before changing assignments.');
90
+ const payload = { operation: 'update', target: { skill_id: skillId, access_revision: native.reference.access_revision },
91
+ revision: native.settingsRevision, patch: { agent_targets: Object.fromEntries(Object.entries(targets).sort(([a], [b]) => a.localeCompare(b))) } };
92
+ const requestId = 'sync-settings-' + createHash('sha256').update(JSON.stringify(payload)).digest('hex');
93
+ const saved = await requestJson<{ receipt_id: string; skill_id: string; target: { skill_id: string }; settings: { agent_targets: AgentTargets; updated_at: string };
94
+ current?: { target: { skill_id: string }; settings: { agent_targets: AgentTargets; updated_at: string } } }>(`${serverUrl}/portal_skills/native-settings`, jwt,
95
+ { body: { ...payload, request_id: requestId } });
96
+ const current = saved.current || saved;
97
+ if (saved.skill_id !== skillId || typeof saved.receipt_id !== 'string' || !saved.receipt_id
98
+ || current.target?.skill_id !== skillId || !current.settings) throw new Error('Native assignment update was not confirmed.');
99
+ return { success: true, agent_targets: current.settings.agent_targets, updated_at: current.settings.updated_at };
100
+ }
101
+ return requestJson<{ success: boolean; agent_targets: AgentTargets; updated_at?: string }>(`${serverUrl}/portal_skills/agent-targets`, jwt, {
78
102
  method: 'PATCH',
79
103
  body: {
80
104
  skill_id: skillId,
81
105
  agent_targets: targets,
106
+ ...(expectedUpdatedAt ? { expected_updated_at: expectedUpdatedAt } : {}),
82
107
  },
83
108
  });
84
109
  }