@dxos/plugin-routine 0.9.1-staging.ee54ba693a → 0.11.0

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 (320) hide show
  1. package/PLUGIN.mdl +18 -13
  2. package/dist/lib/RoutinePlugin.mjs +2 -0
  3. package/dist/lib/RoutinePlugin.node.mjs +894 -0
  4. package/dist/lib/RoutinePlugin.node.mjs.map +1 -0
  5. package/dist/lib/RoutinePlugin.workerd.mjs +20 -0
  6. package/dist/lib/RoutinePlugin.workerd.mjs.map +1 -0
  7. package/dist/lib/capabilities/node.mjs +12 -0
  8. package/dist/lib/capabilities/node.mjs.map +1 -0
  9. package/dist/lib/capabilities.mjs +14 -0
  10. package/dist/lib/capabilities.mjs.map +1 -0
  11. package/dist/lib/chunk-RoutineArticle.mjs +79 -0
  12. package/dist/lib/chunk-RoutineArticle.mjs.map +1 -0
  13. package/dist/lib/chunk-RoutineCompanion.mjs +192 -0
  14. package/dist/lib/chunk-RoutineCompanion.mjs.map +1 -0
  15. package/dist/lib/chunk-RoutinePlugin.mjs +38 -0
  16. package/dist/lib/chunk-RoutinePlugin.mjs.map +1 -0
  17. package/dist/lib/chunk-RoutineSettings.mjs +50 -0
  18. package/dist/lib/chunk-RoutineSettings.mjs.map +1 -0
  19. package/dist/lib/chunk-RoutineTraceCompanion.mjs +208 -0
  20. package/dist/lib/chunk-RoutineTraceCompanion.mjs.map +1 -0
  21. package/dist/lib/chunk-SkillArticle.mjs +28 -0
  22. package/dist/lib/chunk-SkillArticle.mjs.map +1 -0
  23. package/dist/lib/chunk-app-graph-builder.mjs +71 -0
  24. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
  25. package/dist/lib/chunk-create-object.mjs +23 -0
  26. package/dist/lib/chunk-create-object.mjs.map +1 -0
  27. package/dist/lib/chunk-create-routine.mjs +34 -0
  28. package/dist/lib/chunk-create-routine.mjs.map +1 -0
  29. package/dist/lib/chunk-create-trigger-from-template.mjs +48 -0
  30. package/dist/lib/chunk-create-trigger-from-template.mjs.map +1 -0
  31. package/dist/lib/chunk-layer-specs.mjs +179 -0
  32. package/dist/lib/chunk-layer-specs.mjs.map +1 -0
  33. package/dist/lib/{neutral/chunk-HLO4LYXT.mjs → chunk-meta.mjs} +19 -27
  34. package/dist/lib/chunk-meta.mjs.map +1 -0
  35. package/dist/lib/chunk-operation-handler.mjs +11 -0
  36. package/dist/lib/chunk-operation-handler.mjs.map +1 -0
  37. package/dist/lib/chunk-paths.mjs +11 -0
  38. package/dist/lib/chunk-paths.mjs.map +1 -0
  39. package/dist/lib/chunk-react-surface.mjs +64 -0
  40. package/dist/lib/chunk-react-surface.mjs.map +1 -0
  41. package/dist/lib/chunk-registry-sync.mjs +104 -0
  42. package/dist/lib/chunk-registry-sync.mjs.map +1 -0
  43. package/dist/lib/chunk-run-routine.mjs +57 -0
  44. package/dist/lib/chunk-run-routine.mjs.map +1 -0
  45. package/dist/lib/chunk-templates.mjs +37 -0
  46. package/dist/lib/chunk-templates.mjs.map +1 -0
  47. package/dist/lib/chunk-trigger-runtime-controller.mjs +73 -0
  48. package/dist/lib/chunk-trigger-runtime-controller.mjs.map +1 -0
  49. package/dist/lib/chunk-types.mjs +149 -0
  50. package/dist/lib/chunk-types.mjs.map +1 -0
  51. package/dist/lib/chunk-util.mjs +142 -0
  52. package/dist/lib/chunk-util.mjs.map +1 -0
  53. package/dist/lib/components.mjs +1645 -0
  54. package/dist/lib/components.mjs.map +1 -0
  55. package/dist/lib/containers.mjs +11 -0
  56. package/dist/lib/containers.mjs.map +1 -0
  57. package/dist/lib/hooks.mjs +37 -0
  58. package/dist/lib/hooks.mjs.map +1 -0
  59. package/dist/lib/index.mjs +5 -0
  60. package/dist/lib/meta.mjs +2 -0
  61. package/dist/lib/operations.mjs +7 -0
  62. package/dist/lib/operations.mjs.map +1 -0
  63. package/dist/lib/plugin.mjs +9 -0
  64. package/dist/lib/plugin.mjs.map +1 -0
  65. package/dist/lib/testing.mjs +2 -0
  66. package/dist/lib/translations.mjs +110 -0
  67. package/dist/lib/translations.mjs.map +1 -0
  68. package/dist/lib/types.mjs +2 -0
  69. package/dist/types/src/RoutinePlugin.d.ts.map +1 -1
  70. package/dist/types/src/RoutinePlugin.node.d.ts.map +1 -1
  71. package/dist/types/src/RoutinePlugin.workerd.d.ts.map +1 -1
  72. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  73. package/dist/types/src/capabilities/create-object.d.ts +1 -1
  74. package/dist/types/src/capabilities/index.d.ts +0 -1
  75. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  76. package/dist/types/src/capabilities/layer-specs.d.ts +1 -1
  77. package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
  78. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  79. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  80. package/dist/types/src/capabilities/registry-sync.d.ts +1 -1
  81. package/dist/types/src/capabilities/templates.d.ts +1 -1
  82. package/dist/types/src/capabilities/trigger-runtime-controller.d.ts +1 -1
  83. package/dist/types/src/commands/trigger/create/{queue.d.ts → feed.d.ts} +2 -2
  84. package/dist/types/src/commands/trigger/create/feed.d.ts.map +1 -0
  85. package/dist/types/src/commands/trigger/update/{queue.d.ts → feed.d.ts} +2 -2
  86. package/dist/types/src/commands/trigger/update/feed.d.ts.map +1 -0
  87. package/dist/types/src/commands/trigger/util.d.ts +1 -1
  88. package/dist/types/src/commands/trigger/util.d.ts.map +1 -1
  89. package/dist/types/src/components/InstructionsEditor/InstructionsEditor.d.ts +7 -5
  90. package/dist/types/src/components/InstructionsEditor/InstructionsEditor.d.ts.map +1 -1
  91. package/dist/types/src/components/InstructionsEditor/InstructionsEditor.stories.d.ts +3 -3
  92. package/dist/types/src/components/InstructionsEditor/InstructionsEditor.stories.d.ts.map +1 -1
  93. package/dist/types/src/components/RoutineCard/RoutineCard.d.ts +9 -0
  94. package/dist/types/src/components/RoutineCard/RoutineCard.d.ts.map +1 -0
  95. package/dist/types/src/components/RoutineCard/RoutineCard.stories.d.ts +21 -0
  96. package/dist/types/src/components/RoutineCard/RoutineCard.stories.d.ts.map +1 -0
  97. package/dist/types/src/components/RoutineCard/index.d.ts +2 -0
  98. package/dist/types/src/components/RoutineCard/index.d.ts.map +1 -0
  99. package/dist/types/src/components/RoutineForm/RoutineForm.d.ts +7 -2
  100. package/dist/types/src/components/RoutineForm/RoutineForm.d.ts.map +1 -1
  101. package/dist/types/src/components/Schedule/Schedule.d.ts +2 -26
  102. package/dist/types/src/components/Schedule/Schedule.d.ts.map +1 -1
  103. package/dist/types/src/components/Schedule/days.d.ts +24 -0
  104. package/dist/types/src/components/Schedule/days.d.ts.map +1 -0
  105. package/dist/types/src/components/Schedule/describe-schedule.d.ts +3 -0
  106. package/dist/types/src/components/Schedule/describe-schedule.d.ts.map +1 -0
  107. package/dist/types/src/components/Schedule/index.d.ts +2 -0
  108. package/dist/types/src/components/Schedule/index.d.ts.map +1 -1
  109. package/dist/types/src/components/TemplateEditor/TemplateEditor.d.ts +1 -1
  110. package/dist/types/src/components/TemplateEditor/TemplateEditor.d.ts.map +1 -1
  111. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +1 -2
  112. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
  113. package/dist/types/src/components/TriggerEditor/TriggerKindSelector.d.ts +5 -2
  114. package/dist/types/src/components/TriggerEditor/TriggerKindSelector.d.ts.map +1 -1
  115. package/dist/types/src/components/TriggerEditor/trigger-kind-icon.d.ts +5 -0
  116. package/dist/types/src/components/TriggerEditor/trigger-kind-icon.d.ts.map +1 -0
  117. package/dist/types/src/components/index.d.ts +1 -1
  118. package/dist/types/src/components/index.d.ts.map +1 -1
  119. package/dist/types/src/containers/RoutineArticle/RoutineArticle.d.ts +5 -10
  120. package/dist/types/src/containers/RoutineArticle/RoutineArticle.d.ts.map +1 -1
  121. package/dist/types/src/containers/RoutineArticle/RoutineArticle.stories.d.ts +6 -2
  122. package/dist/types/src/containers/RoutineArticle/RoutineArticle.stories.d.ts.map +1 -1
  123. package/dist/types/src/containers/RoutineCompanion/RoutineCompanion.d.ts +4 -12
  124. package/dist/types/src/containers/RoutineCompanion/RoutineCompanion.d.ts.map +1 -1
  125. package/dist/types/src/containers/RoutineCompanion/RoutineCompanion.stories.d.ts.map +1 -1
  126. package/dist/types/src/containers/RoutineSettings/RoutineSettings.d.ts +4 -8
  127. package/dist/types/src/containers/RoutineSettings/RoutineSettings.d.ts.map +1 -1
  128. package/dist/types/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.d.ts +5 -3
  129. package/dist/types/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.d.ts.map +1 -1
  130. package/dist/types/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.stories.d.ts.map +1 -1
  131. package/dist/types/src/containers/RoutineTraceCompanion/useRoutineRuns.d.ts +1 -1
  132. package/dist/types/src/containers/RoutineTraceCompanion/useRoutineRuns.d.ts.map +1 -1
  133. package/dist/types/src/containers/SkillArticle/SkillArticle.d.ts +4 -1
  134. package/dist/types/src/containers/SkillArticle/SkillArticle.d.ts.map +1 -1
  135. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts +1 -1
  136. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts.map +1 -1
  137. package/dist/types/src/index.d.ts +1 -0
  138. package/dist/types/src/index.d.ts.map +1 -1
  139. package/dist/types/src/operations/run-routine.d.ts.map +1 -1
  140. package/dist/types/src/operations/run-routine.test.d.ts +2 -0
  141. package/dist/types/src/operations/run-routine.test.d.ts.map +1 -0
  142. package/dist/types/src/paths.d.ts.map +1 -1
  143. package/dist/types/src/templates/index.d.ts +1 -1
  144. package/dist/types/src/templates/index.d.ts.map +1 -1
  145. package/dist/types/src/testing/test-functions.d.ts +30 -6
  146. package/dist/types/src/testing/test-functions.d.ts.map +1 -1
  147. package/dist/types/src/translations.d.ts.map +1 -1
  148. package/dist/types/src/types/RoutineCapabilities.d.ts +9 -3
  149. package/dist/types/src/types/RoutineCapabilities.d.ts.map +1 -1
  150. package/dist/types/src/types/RoutineOperation.d.ts +2 -3
  151. package/dist/types/src/types/RoutineOperation.d.ts.map +1 -1
  152. package/dist/types/src/types/index.d.ts +0 -1
  153. package/dist/types/src/types/index.d.ts.map +1 -1
  154. package/dist/types/src/util/index.d.ts +1 -0
  155. package/dist/types/src/util/index.d.ts.map +1 -1
  156. package/dist/types/src/util/routines-for-object.d.ts +6 -3
  157. package/dist/types/src/util/routines-for-object.d.ts.map +1 -1
  158. package/dist/types/src/util/wire.d.ts +21 -0
  159. package/dist/types/src/util/wire.d.ts.map +1 -0
  160. package/dist/types/src/util/wire.test.d.ts +2 -0
  161. package/dist/types/src/util/wire.test.d.ts.map +1 -0
  162. package/dist/types/tsconfig.tsbuildinfo +1 -1
  163. package/dx.config.ts +3 -2
  164. package/package.json +73 -71
  165. package/src/RoutinePlugin.node.ts +1 -2
  166. package/src/RoutinePlugin.tsx +2 -4
  167. package/src/RoutinePlugin.workerd.ts +1 -2
  168. package/src/capabilities/app-graph-builder.ts +9 -7
  169. package/src/capabilities/create-object.ts +2 -2
  170. package/src/capabilities/index.ts +0 -1
  171. package/src/capabilities/layer-specs.ts +78 -12
  172. package/src/capabilities/{react-surface.tsx → react-surface.ts} +18 -22
  173. package/src/capabilities/trigger-runtime-controller.test.ts +12 -18
  174. package/src/capabilities/trigger-runtime-controller.ts +22 -25
  175. package/src/commands/trigger/create/{queue.ts → feed.ts} +1 -1
  176. package/src/commands/trigger/create/index.ts +2 -2
  177. package/src/commands/trigger/update/{queue.ts → feed.ts} +1 -1
  178. package/src/commands/trigger/update/index.ts +2 -2
  179. package/src/commands/trigger/util.ts +1 -2
  180. package/src/components/InstructionsEditor/InstructionsEditor.stories.tsx +7 -6
  181. package/src/components/InstructionsEditor/InstructionsEditor.tsx +31 -19
  182. package/src/components/RoutineCard/RoutineCard.stories.tsx +95 -0
  183. package/src/components/RoutineCard/RoutineCard.tsx +60 -0
  184. package/src/components/{MasterDetail → RoutineCard}/index.ts +1 -1
  185. package/src/components/RoutineForm/RoutineForm.tsx +60 -52
  186. package/src/components/Schedule/Schedule.stories.tsx +1 -1
  187. package/src/components/Schedule/Schedule.tsx +4 -73
  188. package/src/components/Schedule/days.ts +18 -0
  189. package/src/components/Schedule/describe-schedule.ts +64 -0
  190. package/src/components/Schedule/index.ts +2 -0
  191. package/src/components/TemplateEditor/TemplateEditor.stories.tsx +1 -1
  192. package/src/components/TemplateEditor/TemplateForm.tsx +1 -1
  193. package/src/components/TriggerEditor/TriggerEditor.stories.tsx +3 -3
  194. package/src/components/TriggerEditor/TriggerEditor.tsx +79 -66
  195. package/src/components/TriggerEditor/TriggerKindSelector.tsx +4 -30
  196. package/src/components/TriggerEditor/trigger-kind-icon.ts +38 -0
  197. package/src/components/index.ts +1 -1
  198. package/src/containers/RoutineArticle/RoutineArticle.stories.tsx +78 -19
  199. package/src/containers/RoutineArticle/RoutineArticle.tsx +8 -4
  200. package/src/containers/RoutineCompanion/RoutineCompanion.stories.tsx +5 -4
  201. package/src/containers/RoutineCompanion/RoutineCompanion.tsx +11 -7
  202. package/src/containers/RoutineSettings/RoutineSettings.tsx +22 -33
  203. package/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.stories.tsx +6 -5
  204. package/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.tsx +3 -1
  205. package/src/containers/RoutineTraceCompanion/useRoutineRuns.ts +3 -5
  206. package/src/containers/SkillArticle/SkillArticle.tsx +2 -0
  207. package/src/hooks/useTriggerRuntimeControls.ts +2 -2
  208. package/src/index.ts +1 -0
  209. package/src/operations/create-routine.ts +1 -1
  210. package/src/operations/create-trigger-from-template.ts +2 -2
  211. package/src/operations/run-routine.test.ts +150 -0
  212. package/src/operations/run-routine.ts +18 -2
  213. package/src/paths.ts +5 -5
  214. package/src/templates/index.ts +5 -3
  215. package/src/translations.ts +5 -6
  216. package/src/types/RoutineCapabilities.ts +10 -4
  217. package/src/types/RoutineOperation.ts +7 -6
  218. package/src/types/index.ts +0 -1
  219. package/src/util/index.ts +1 -0
  220. package/src/util/routines-for-object.test.ts +37 -5
  221. package/src/util/routines-for-object.ts +33 -8
  222. package/src/util/wire.test.ts +77 -0
  223. package/src/util/wire.ts +76 -0
  224. package/dist/lib/neutral/RoutineArticle-UNTMV4CC.mjs +0 -95
  225. package/dist/lib/neutral/RoutineArticle-UNTMV4CC.mjs.map +0 -7
  226. package/dist/lib/neutral/RoutineCompanion-ZZ6WMY7I.mjs +0 -249
  227. package/dist/lib/neutral/RoutineCompanion-ZZ6WMY7I.mjs.map +0 -7
  228. package/dist/lib/neutral/RoutinePlugin.mjs +0 -10
  229. package/dist/lib/neutral/RoutinePlugin.mjs.map +0 -7
  230. package/dist/lib/neutral/RoutinePlugin.node.mjs +0 -1133
  231. package/dist/lib/neutral/RoutinePlugin.node.mjs.map +0 -7
  232. package/dist/lib/neutral/RoutinePlugin.workerd.mjs +0 -36
  233. package/dist/lib/neutral/RoutinePlugin.workerd.mjs.map +0 -7
  234. package/dist/lib/neutral/RoutineSettings-ZJOM4F3S.mjs +0 -48
  235. package/dist/lib/neutral/RoutineSettings-ZJOM4F3S.mjs.map +0 -7
  236. package/dist/lib/neutral/RoutineTraceCompanion-5U6R7LJE.mjs +0 -236
  237. package/dist/lib/neutral/RoutineTraceCompanion-5U6R7LJE.mjs.map +0 -7
  238. package/dist/lib/neutral/SkillArticle-WWDNKW2Z.mjs +0 -27
  239. package/dist/lib/neutral/SkillArticle-WWDNKW2Z.mjs.map +0 -7
  240. package/dist/lib/neutral/app-graph-builder-FALWLZTI.mjs +0 -90
  241. package/dist/lib/neutral/app-graph-builder-FALWLZTI.mjs.map +0 -7
  242. package/dist/lib/neutral/capabilities/index.mjs +0 -25
  243. package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
  244. package/dist/lib/neutral/capabilities/node.mjs +0 -19
  245. package/dist/lib/neutral/capabilities/node.mjs.map +0 -7
  246. package/dist/lib/neutral/chunk-674LDIUJ.mjs +0 -9
  247. package/dist/lib/neutral/chunk-674LDIUJ.mjs.map +0 -7
  248. package/dist/lib/neutral/chunk-HLO4LYXT.mjs.map +0 -7
  249. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
  250. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
  251. package/dist/lib/neutral/chunk-P3TDQKOO.mjs +0 -8
  252. package/dist/lib/neutral/chunk-P3TDQKOO.mjs.map +0 -7
  253. package/dist/lib/neutral/chunk-R7J2MU2M.mjs +0 -263
  254. package/dist/lib/neutral/chunk-R7J2MU2M.mjs.map +0 -7
  255. package/dist/lib/neutral/chunk-WLNL77C7.mjs +0 -14
  256. package/dist/lib/neutral/chunk-WLNL77C7.mjs.map +0 -7
  257. package/dist/lib/neutral/chunk-YGWSAKLD.mjs +0 -65
  258. package/dist/lib/neutral/chunk-YGWSAKLD.mjs.map +0 -7
  259. package/dist/lib/neutral/components/index.mjs +0 -1921
  260. package/dist/lib/neutral/components/index.mjs.map +0 -7
  261. package/dist/lib/neutral/containers/index.mjs +0 -17
  262. package/dist/lib/neutral/containers/index.mjs.map +0 -7
  263. package/dist/lib/neutral/create-object-V23KVDMJ.mjs +0 -25
  264. package/dist/lib/neutral/create-object-V23KVDMJ.mjs.map +0 -7
  265. package/dist/lib/neutral/create-routine-6QBY6TWF.mjs +0 -38
  266. package/dist/lib/neutral/create-routine-6QBY6TWF.mjs.map +0 -7
  267. package/dist/lib/neutral/create-trigger-from-template-U3KNS77V.mjs +0 -70
  268. package/dist/lib/neutral/create-trigger-from-template-U3KNS77V.mjs.map +0 -7
  269. package/dist/lib/neutral/hooks/index.mjs +0 -47
  270. package/dist/lib/neutral/hooks/index.mjs.map +0 -7
  271. package/dist/lib/neutral/index.mjs +0 -27
  272. package/dist/lib/neutral/index.mjs.map +0 -7
  273. package/dist/lib/neutral/layer-specs-2TWJU5HI.mjs +0 -135
  274. package/dist/lib/neutral/layer-specs-2TWJU5HI.mjs.map +0 -7
  275. package/dist/lib/neutral/meta.json +0 -1
  276. package/dist/lib/neutral/meta.mjs +0 -8
  277. package/dist/lib/neutral/meta.mjs.map +0 -7
  278. package/dist/lib/neutral/navigation-resolver-KQIWUH6F.mjs +0 -18
  279. package/dist/lib/neutral/navigation-resolver-KQIWUH6F.mjs.map +0 -7
  280. package/dist/lib/neutral/operation-handler-QKLJSDCQ.mjs +0 -13
  281. package/dist/lib/neutral/operation-handler-QKLJSDCQ.mjs.map +0 -7
  282. package/dist/lib/neutral/operations/index.mjs +0 -8
  283. package/dist/lib/neutral/operations/index.mjs.map +0 -7
  284. package/dist/lib/neutral/plugin.mjs +0 -16
  285. package/dist/lib/neutral/plugin.mjs.map +0 -7
  286. package/dist/lib/neutral/react-surface-GMO7I7YE.mjs +0 -67
  287. package/dist/lib/neutral/react-surface-GMO7I7YE.mjs.map +0 -7
  288. package/dist/lib/neutral/registry-sync-DYPOC7ZG.mjs +0 -86
  289. package/dist/lib/neutral/registry-sync-DYPOC7ZG.mjs.map +0 -7
  290. package/dist/lib/neutral/run-routine-AEWO6SYD.mjs +0 -37
  291. package/dist/lib/neutral/run-routine-AEWO6SYD.mjs.map +0 -7
  292. package/dist/lib/neutral/templates-IIUCEBAX.mjs +0 -49
  293. package/dist/lib/neutral/templates-IIUCEBAX.mjs.map +0 -7
  294. package/dist/lib/neutral/testing.mjs +0 -8
  295. package/dist/lib/neutral/testing.mjs.map +0 -7
  296. package/dist/lib/neutral/translations.mjs +0 -115
  297. package/dist/lib/neutral/translations.mjs.map +0 -7
  298. package/dist/lib/neutral/trigger-runtime-controller-WIDDFGQT.mjs +0 -77
  299. package/dist/lib/neutral/trigger-runtime-controller-WIDDFGQT.mjs.map +0 -7
  300. package/dist/lib/neutral/types/index.mjs +0 -17
  301. package/dist/lib/neutral/types/index.mjs.map +0 -7
  302. package/dist/types/src/capabilities/navigation-resolver.d.ts +0 -5
  303. package/dist/types/src/capabilities/navigation-resolver.d.ts.map +0 -1
  304. package/dist/types/src/commands/trigger/create/queue.d.ts.map +0 -1
  305. package/dist/types/src/commands/trigger/update/queue.d.ts.map +0 -1
  306. package/dist/types/src/components/MasterDetail/MasterDetail.d.ts +0 -51
  307. package/dist/types/src/components/MasterDetail/MasterDetail.d.ts.map +0 -1
  308. package/dist/types/src/components/MasterDetail/MasterDetail.stories.d.ts +0 -16
  309. package/dist/types/src/components/MasterDetail/MasterDetail.stories.d.ts.map +0 -1
  310. package/dist/types/src/components/MasterDetail/index.d.ts +0 -2
  311. package/dist/types/src/components/MasterDetail/index.d.ts.map +0 -1
  312. package/dist/types/src/types/Routine.d.ts +0 -55
  313. package/dist/types/src/types/Routine.d.ts.map +0 -1
  314. package/dist/types/src/types/Routine.test.d.ts +0 -2
  315. package/dist/types/src/types/Routine.test.d.ts.map +0 -1
  316. package/src/capabilities/navigation-resolver.ts +0 -21
  317. package/src/components/MasterDetail/MasterDetail.stories.tsx +0 -100
  318. package/src/components/MasterDetail/MasterDetail.tsx +0 -175
  319. package/src/types/Routine.test.ts +0 -51
  320. package/src/types/Routine.ts +0 -144
@@ -0,0 +1,150 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { Atom, Registry } from '@effect-atom/atom';
6
+ import { describe, it } from '@effect/vitest';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Layer from 'effect/Layer';
9
+ import * as Schema from 'effect/Schema';
10
+
11
+ import { AssistantTestLayer } from '@dxos/agent-runtime/testing';
12
+ import { Capability, CapabilityManager } from '@dxos/app-framework';
13
+ import { Operation, OperationHandlerSet, Routine, Trigger } from '@dxos/compute';
14
+ import { Database, DXN, Ref } from '@dxos/echo';
15
+ import { TestHelpers } from '@dxos/effect/testing';
16
+
17
+ import { RoutineOperation } from '../types';
18
+ import { makeRoutine } from '../util';
19
+ import RunRoutineHandler from './run-routine';
20
+
21
+ /** Captures the input each run receives so the test can assert on it after the invocation. */
22
+ const received: unknown[] = [];
23
+
24
+ /** Trigger ids the monitor was asked to invoke — the seam a `remote` trigger must reach instead of running here. */
25
+ const monitorInvocations: string[] = [];
26
+
27
+ /** Stands in for the aggregate monitor, whose real implementation routes `remote` triggers to EDGE over HTTP. */
28
+ const TestMonitor = Layer.succeed(Trigger.TriggerMonitorService, {
29
+ triggers: Atom.make<readonly Trigger.State[]>([]),
30
+ localDispatcherEnabled: false,
31
+ invokeTrigger: ({ trigger }) => Effect.sync(() => void monitorInvocations.push(trigger.id)),
32
+ });
33
+
34
+ /**
35
+ * Stand-in for a connector's sync operation: like `InboxOperation.GoogleMailSync` it destructures a
36
+ * required input, so a run that supplies none throws before the handler body.
37
+ */
38
+ const TestRunnable = Operation.make({
39
+ meta: { key: DXN.make('org.dxos.test.runnable'), name: 'Test Runnable' },
40
+ input: Schema.Struct({ label: Schema.Any }),
41
+ output: Schema.Void,
42
+ });
43
+
44
+ const TestLayer = AssistantTestLayer({
45
+ operationHandlers: OperationHandlerSet.make(
46
+ RunRoutineHandler,
47
+ TestRunnable.pipe(
48
+ Operation.withHandler(({ label }) => {
49
+ received.push(label);
50
+ return Effect.void;
51
+ }),
52
+ ),
53
+ ),
54
+ types: [Routine.Routine, Trigger.Trigger, Operation.PersistentOperation],
55
+ disableLlmMemoization: true,
56
+ // `RunRoutine` declares `Capability.Service`; an empty manager discharges it (the handler never reads it).
57
+ extraServices: Layer.mergeAll(
58
+ Layer.succeed(Capability.Service, CapabilityManager.make({ registry: Registry.make() })),
59
+ TestMonitor,
60
+ ),
61
+ });
62
+
63
+ describe('RunRoutine', () => {
64
+ it.effect(
65
+ 'passes the trigger input to the runnable',
66
+ Effect.fnUntraced(
67
+ function* ({ expect }) {
68
+ received.length = 0;
69
+ const { routine } = yield* addRoutine({ label: 'from-trigger' });
70
+
71
+ yield* Operation.invoke(RoutineOperation.RunRoutine, { routine: Ref.make(routine) });
72
+
73
+ expect(received).toEqual(['from-trigger']);
74
+ },
75
+ Effect.provide(TestLayer),
76
+ TestHelpers.provideTestContext,
77
+ ),
78
+ );
79
+
80
+ it.effect(
81
+ 'resolves an event template against the synthetic run event',
82
+ Effect.fnUntraced(
83
+ function* ({ expect }) {
84
+ received.length = 0;
85
+ const { routine } = yield* addRoutine({ label: '{{event.tick}}' });
86
+
87
+ yield* Operation.invoke(RoutineOperation.RunRoutine, { routine: Ref.make(routine) });
88
+
89
+ expect(received).toHaveLength(1);
90
+ expect(received[0]).toBeTypeOf('number');
91
+ },
92
+ Effect.provide(TestLayer),
93
+ TestHelpers.provideTestContext,
94
+ ),
95
+ );
96
+
97
+ it.effect(
98
+ 'routes a remote trigger to the monitor instead of running the runnable locally',
99
+ Effect.fnUntraced(
100
+ function* ({ expect }) {
101
+ received.length = 0;
102
+ monitorInvocations.length = 0;
103
+ const { routine, trigger } = yield* addRoutine({ label: 'on-edge' }, true);
104
+
105
+ yield* Operation.invoke(RoutineOperation.RunRoutine, { routine: Ref.make(routine) });
106
+
107
+ // The monitor sends it to the EDGE dispatcher; nothing runs in this process.
108
+ expect(monitorInvocations).toEqual([trigger.id]);
109
+ expect(received).toEqual([]);
110
+ },
111
+ Effect.provide(TestLayer),
112
+ TestHelpers.provideTestContext,
113
+ ),
114
+ );
115
+
116
+ it.effect(
117
+ 'runs a local trigger in-process without touching the monitor',
118
+ Effect.fnUntraced(
119
+ function* ({ expect }) {
120
+ received.length = 0;
121
+ monitorInvocations.length = 0;
122
+ const { routine } = yield* addRoutine({ label: 'on-client' });
123
+
124
+ yield* Operation.invoke(RoutineOperation.RunRoutine, { routine: Ref.make(routine) });
125
+
126
+ expect(received).toEqual(['on-client']);
127
+ expect(monitorInvocations).toEqual([]);
128
+ },
129
+ Effect.provide(TestLayer),
130
+ TestHelpers.provideTestContext,
131
+ ),
132
+ );
133
+ });
134
+
135
+ /**
136
+ * A routine whose action is `TestRunnable`, with the runnable's input carried on its timer trigger.
137
+ * Returns the trigger alongside so assertions can name its id without re-deriving it from the ref.
138
+ */
139
+ const addRoutine = Effect.fnUntraced(function* (input: Record<string, unknown>, remote?: boolean) {
140
+ const operation = yield* Database.add(Operation.serialize(TestRunnable));
141
+ const trigger = Trigger.make({ enabled: true, remote, spec: Trigger.specTimer('*/10 * * * *'), input });
142
+ const routine = makeRoutine({
143
+ name: 'Test',
144
+ spec: { kind: 'runnable', runnable: Ref.make(operation) },
145
+ trigger,
146
+ });
147
+ yield* Database.add(routine);
148
+ yield* Database.flush();
149
+ return { routine, trigger };
150
+ });
@@ -5,7 +5,8 @@
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
7
  import { RunInstructions } from '@dxos/assistant-toolkit';
8
- import { Operation } from '@dxos/compute';
8
+ import { Operation, Trigger } from '@dxos/compute';
9
+ import { createInvocationPayload } from '@dxos/compute-runtime';
9
10
  import { Database, Obj } from '@dxos/echo';
10
11
  import { invariant } from '@dxos/invariant';
11
12
 
@@ -20,6 +21,18 @@ const handler: Operation.WithHandler<typeof RoutineOperation.RunRoutine> = Routi
20
21
  const routineObj = yield* Database.load(routine);
21
22
  const db = Obj.getDatabase(routineObj);
22
23
  invariant(db, 'Routine is not attached to a database.');
24
+ // Triggers, not routines, carry the runnable's input binding (e.g. a sync routine's `binding`
25
+ // cursor) and the `remote` flag, so a manual run goes through the first trigger — the one it is
26
+ // also attributed to.
27
+ const trigger = routineObj.triggers[0] ? yield* Database.load(routineObj.triggers[0]) : undefined;
28
+
29
+ // An edge routine is registered only on the EDGE dispatcher, so running it means force-running that
30
+ // trigger over HTTP; invoking the runnable here would silently run it on the client instead.
31
+ if (trigger?.remote === true) {
32
+ const monitor = yield* Trigger.TriggerMonitorService;
33
+ yield* monitor.invokeTrigger({ trigger, event: { tick: Date.now() } });
34
+ return;
35
+ }
23
36
 
24
37
  // The action kind is explicit in `spec`; dispatch by kind rather than dereferencing to classify.
25
38
  const spec = routineObj.spec;
@@ -37,7 +50,10 @@ const handler: Operation.WithHandler<typeof RoutineOperation.RunRoutine> = Routi
37
50
  invariant(spec?.kind === 'runnable', 'Routine has no action to run.');
38
51
  const operation = yield* Database.load(spec.runnable);
39
52
  invariant(Obj.instanceOf(Operation.PersistentOperation, operation), 'Routine has no action to run.');
40
- yield* Operation.invoke(Operation.deserialize(operation));
53
+ // Built by the dispatcher's own payload builder against a synthetic tick, so a manual run resolves
54
+ // `{{event.*}}`/`{{trigger.*}}` templates exactly as a scheduled fire would.
55
+ const input = trigger ? createInvocationPayload(trigger, { tick: Date.now() }) : undefined;
56
+ yield* Operation.invoke(Operation.deserialize(operation), input);
41
57
  }),
42
58
  ),
43
59
  );
package/src/paths.ts CHANGED
@@ -2,17 +2,17 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { Paths } from '@dxos/app-toolkit';
5
+ import { GraphPath } from '@dxos/app-toolkit';
6
+ import { Routine } from '@dxos/compute';
6
7
 
7
8
  import { meta } from '#meta';
8
- import { Routine } from '#types';
9
9
 
10
- const { getSectionPath: getRoutinesPath } = Paths.createTypeSectionPaths(Routine.Routine, {
11
- groupId: Paths.GroupSegments.ai,
10
+ const { getSectionPath: getRoutinesPath } = GraphPath.createTypeSectionPaths(Routine.Routine, {
11
+ groupId: GraphPath.GroupSegments.ai,
12
12
  });
13
13
 
14
14
  /** Path to the routines settings section for a given space. */
15
15
  export const getRoutinesSettingsPath = (spaceId: string): string =>
16
- Paths.getSpacePath(spaceId, 'settings', `${meta.profile.key}.routines`);
16
+ GraphPath.getSpacePath(spaceId, 'settings', `${meta.profile.key}.routines`);
17
17
 
18
18
  export { getRoutinesPath };
@@ -9,7 +9,9 @@ import { AppAnnotation } from '@dxos/app-toolkit';
9
9
  import { Instructions, Skill, Trigger } from '@dxos/compute';
10
10
  import { Obj, Ref, Type } from '@dxos/echo';
11
11
 
12
- import { Routine, type RoutineCapabilities } from '#types';
12
+ import { RoutineCapabilities } from '#types';
13
+
14
+ import { makeRoutine } from '../util';
13
15
 
14
16
  /**
15
17
  * Blank template: an instructions-action routine draft with an empty trigger; the action kind, schedule, and
@@ -18,12 +20,12 @@ import { Routine, type RoutineCapabilities } from '#types';
18
20
  * (no subject) starts with bare instructions.
19
21
  */
20
22
  export const blank: RoutineCapabilities.Template = {
21
- id: 'org.dxos.routine.blank',
23
+ id: RoutineCapabilities.BlankTemplateId,
22
24
  label: 'Blank',
23
25
  icon: 'ph--lightning--regular',
24
26
  scaffold: ({ name, subject }) =>
25
27
  Effect.succeed(
26
- Routine.make({
28
+ makeRoutine({
27
29
  name,
28
30
  instructions: Instructions.make({
29
31
  skills: subject ? skillRefsForObject(subject) : [],
@@ -2,13 +2,13 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { Routine } from '@dxos/compute';
5
6
  import { Type } from '@dxos/echo';
6
7
  import { type Resource } from '@dxos/react-ui';
7
8
  import { translations as componentsTranslations } from '@dxos/react-ui-components/translations';
8
9
  import { translations as formTranslations } from '@dxos/react-ui-form/translations';
9
10
 
10
11
  import { meta } from '#meta';
11
- import { Routine } from '#types';
12
12
 
13
13
  export const translations: Resource[] = [
14
14
  ...componentsTranslations,
@@ -75,6 +75,8 @@ export const translations: Resource[] = [
75
75
  'trigger-kind.feed.description': 'Run when a new item arrives in a feed.',
76
76
  'trigger-kind.email.label': 'Email',
77
77
  'trigger-kind.email.description': 'Run when an email is received.',
78
+ 'trigger-kind.direct.label': 'Direct',
79
+ 'trigger-kind.direct.description': 'Run only when invoked directly.',
78
80
  'trigger-kind.clear.label': 'Clear',
79
81
  'trigger-kind.query-stub.message': 'Query editing is not yet available.',
80
82
  'trigger-kind.email-note.message': 'Runs whenever a new email is received.',
@@ -114,11 +116,8 @@ export const translations: Resource[] = [
114
116
  'routine-verbose.label': 'Manage routines',
115
117
  'routine.description': 'Manage where routines in this space run.',
116
118
 
117
- 'runtime.label': 'Runtime location',
118
- 'runtime.description': 'Determines where routines in this space run.',
119
- 'runtime.disabled.label': 'Disabled',
120
- 'runtime.local.label': 'Local',
121
- 'runtime.edge.label': 'EDGE (experimental)',
119
+ 'runtime.label': 'Enable triggers',
120
+ 'runtime.description': "Run this space's triggers. Individual triggers can be routed to the edge.",
122
121
  },
123
122
  },
124
123
  },
@@ -4,11 +4,10 @@
4
4
 
5
5
  import type * as Effect from 'effect/Effect';
6
6
 
7
+ import type { DelegationStrategy } from '@dxos/agent-runtime';
7
8
  import { Capability } from '@dxos/app-framework';
9
+ import type { Routine } from '@dxos/compute';
8
10
  import type { Database, Obj } from '@dxos/echo';
9
- import type { DelegationStrategy } from '@dxos/functions-runtime';
10
-
11
- import * as Routine from './Routine';
12
11
 
13
12
  /**
14
13
  * Optional supervisor strategy for the agent chat service. When contributed (by a plugin that knows
@@ -20,6 +19,13 @@ export const AgentDelegationStrategy = Capability.make<DelegationStrategy>(
20
19
  'org.dxos.plugin.routine.capability.agentDelegationStrategy',
21
20
  );
22
21
 
22
+ /**
23
+ * Id of the built-in blank template. Declared here (rather than on the template itself) so callers that
24
+ * scaffold a routine without the picker — e.g. a project's toolbar — can name it without importing the
25
+ * template module.
26
+ */
27
+ export const BlankTemplateId = 'org.dxos.routine.blank';
28
+
23
29
  /**
24
30
  * An automation template contributed by a plugin. The create dialog and the per-object "Automations"
25
31
  * companion list contributed templates (`Capability.getAll(RoutineCapabilities.Template)`) and run the
@@ -40,7 +46,7 @@ export type Template = {
40
46
  appliesTo?: (subject?: Obj.Unknown) => boolean;
41
47
  /**
42
48
  * Build the routine as a fully-wired in-memory {@link Routine.Routine} graph — the routine plus its owned
43
- * trigger and instructions, assembled by `Routine.make`. The create flow persists it with a single
49
+ * trigger and instructions, assembled by `makeRoutine`. The create flow persists it with a single
44
50
  * `Database.add` (which cascades the owned children); scaffold must NOT call `Database.add` itself.
45
51
  * `Database.Service` may still be used for read-only lookups (e.g. loading a feed ref). `subject` is set
46
52
  * when scaffolding from an object's companion.
@@ -8,7 +8,7 @@ import * as Schema from 'effect/Schema';
8
8
 
9
9
  import { Capability } from '@dxos/app-framework';
10
10
  import { Chat } from '@dxos/assistant-toolkit';
11
- import { Instructions, Operation } from '@dxos/compute';
11
+ import { Instructions, Operation, Routine, Trigger } from '@dxos/compute';
12
12
  import { Database, DXN, Obj, Ref, Type } from '@dxos/echo';
13
13
  // Value-side `EID` import keeps TS declaration emit portable — `TriggerTemplate`
14
14
  // references `EID.Schema` and the inferred `CreateTriggerFromTemplate` type
@@ -17,7 +17,6 @@ import { EID as _EchoURIReference } from '@dxos/keys';
17
17
 
18
18
  import { meta } from '#meta';
19
19
 
20
- import * as Routine from './Routine';
21
20
  import { TriggerTemplate } from './schema';
22
21
  export { _EchoURIReference };
23
22
 
@@ -87,16 +86,18 @@ export const RunPromptInNewChat = Operation.make({
87
86
  }),
88
87
  });
89
88
 
90
- // Runs a routine's `runnable` directly (bypassing its triggers). The runnable receives no input;
91
- // the trigger-driven path is what supplies event-mapped input, so manual runs target runnables that
92
- // need none.
89
+ // Runs a routine's action now, routed by its first trigger — that is where both the runnable's input
90
+ // (e.g. a sync routine's binding cursor) and the `remote` flag live. A `remote` trigger force-runs on the
91
+ // EDGE dispatcher over HTTP, since that is the only runtime it is registered on; a local one runs
92
+ // in-process. Without the trigger a manual run would invoke an input-taking runnable with nothing, and
93
+ // would silently run an edge routine on the client.
93
94
  export const RunRoutine = Operation.make({
94
95
  meta: {
95
96
  key: makeKey('runAutomation'),
96
97
  name: 'Run Routine',
97
98
  icon: 'ph--play--regular',
98
99
  },
99
- services: [Capability.Service],
100
+ services: [Capability.Service, Trigger.TriggerMonitorService],
100
101
  input: Schema.Struct({
101
102
  routine: Ref.Ref(Routine.Routine),
102
103
  }),
@@ -4,6 +4,5 @@
4
4
 
5
5
  export * from './schema';
6
6
 
7
- export * as Routine from './Routine';
8
7
  export * as RoutineCapabilities from './RoutineCapabilities';
9
8
  export * as RoutineOperation from './RoutineOperation';
package/src/util/index.ts CHANGED
@@ -4,3 +4,4 @@
4
4
 
5
5
  export * from './routines-for-object';
6
6
  export * from './run-instructions';
7
+ export * from './wire';
@@ -5,17 +5,17 @@
5
5
  import * as Schema from 'effect/Schema';
6
6
  import { describe, test } from 'vitest';
7
7
 
8
- import { Instructions, Trigger } from '@dxos/compute';
8
+ import { Instructions, Routine, Trigger } from '@dxos/compute';
9
9
  import { type Database, DXN, Feed, Obj, Ref, Type } from '@dxos/echo';
10
10
  import { EffectEx } from '@dxos/effect';
11
11
  import { EID, URI } from '@dxos/keys';
12
+ import { AccessToken, Cursor } from '@dxos/link';
12
13
  import { ClientCapabilities, ClientEvents } from '@dxos/plugin-client';
13
14
  import { ClientPlugin, initializeIdentity } from '@dxos/plugin-client/testing';
14
15
  import { createComposerTestApp } from '@dxos/plugin-testing/harness';
15
16
 
16
- import { Routine } from '#types';
17
-
18
17
  import { connectedRoutinesQuery, routinesForObject } from './routines-for-object';
18
+ import { makeRoutine } from './wire';
19
19
 
20
20
  // A minimal feed-annotated host (like a mailbox): an object holding a `feed` ref.
21
21
  const FeedHost = Type.makeObject(DXN.make('org.dxos.test.feedHost', '0.1.0'))(
@@ -25,7 +25,15 @@ const FeedHost = Type.makeObject(DXN.make('org.dxos.test.feedHost', '0.1.0'))(
25
25
  }),
26
26
  );
27
27
 
28
- const types = [Routine.Routine, Trigger.Trigger, Instructions.Instructions, Feed.Feed, FeedHost];
28
+ const types = [
29
+ Routine.Routine,
30
+ Trigger.Trigger,
31
+ Instructions.Instructions,
32
+ Feed.Feed,
33
+ FeedHost,
34
+ Cursor.Cursor,
35
+ AccessToken.AccessToken,
36
+ ];
29
37
 
30
38
  const initSpace = async (harness: Awaited<ReturnType<typeof createComposerTestApp>>) => {
31
39
  const { personalSpace } = await EffectEx.runAndForwardErrors(
@@ -68,6 +76,30 @@ describe('routines connected to an object', () => {
68
76
  await expect.poll(() => connectedIds(db, other), { timeout: 5_000 }).toEqual([]);
69
77
  });
70
78
 
79
+ test('cursor-ref: trigger bound (via `binding`) to a Cursor whose `spec.target` is the object', async ({
80
+ expect,
81
+ }) => {
82
+ await using harness = await createComposerTestApp({ plugins: [ClientPlugin({ types })] });
83
+ const db = await initSpace(harness);
84
+
85
+ const target = db.add(Routine.make({ name: 'target', triggers: [] }));
86
+ const other = db.add(Routine.make({ name: 'other', triggers: [] }));
87
+ // A sync trigger references its target only indirectly: `input.binding` → Cursor → `spec.target` → target.
88
+ const token = db.add(AccessToken.make({ source: 'github.com', token: 'tok' }));
89
+ const cursor = db.add(Cursor.makeExternal({ source: Ref.make(token), target: qualifiedRef(db, target) }));
90
+ const trigger = db.add(
91
+ Trigger.make({ spec: Trigger.specTimer('*/10 * * * *'), input: { binding: qualifiedRef(db, cursor) } }),
92
+ );
93
+ const owner = db.add(Routine.make({ name: 'owner', triggers: [Ref.make(trigger)] }));
94
+ await db.flush();
95
+
96
+ expect(routinesForObject(target, [owner, other]).map((routine) => routine.id)).toEqual([owner.id]);
97
+ expect(routinesForObject(other, [owner])).toEqual([]);
98
+
99
+ await expect.poll(() => connectedIds(db, target), { timeout: 5_000 }).toEqual([owner.id]);
100
+ await expect.poll(() => connectedIds(db, other), { timeout: 5_000 }).toEqual([]);
101
+ });
102
+
71
103
  test("feed-ref: feed trigger bound to the object's feed", async ({ expect }) => {
72
104
  await using harness = await createComposerTestApp({ plugins: [ClientPlugin({ types })] });
73
105
  const db = await initSpace(harness);
@@ -97,7 +129,7 @@ describe('routines connected to an object', () => {
97
129
  // The routine's action is owned instructions that bind the object as a context object — the second
98
130
  // connection path (O ← Instructions.objects ← Routine via spec.instructions), with no trigger involved.
99
131
  const instructions = db.add(Instructions.make({ objects: [qualifiedRef(db, target)] }));
100
- const owner = db.add(Routine.make({ name: 'owner', instructions }));
132
+ const owner = db.add(makeRoutine({ name: 'owner', instructions }));
101
133
  await db.flush();
102
134
 
103
135
  expect(routinesForObject(target, [owner, other]).map((routine) => routine.id)).toEqual([owner.id]);
@@ -2,17 +2,19 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { Instructions, Trigger } from '@dxos/compute';
5
+ import { Instructions, Routine, Trigger } from '@dxos/compute';
6
6
  import { Filter, Obj, Query, Ref } from '@dxos/echo';
7
-
8
- import { Routine } from '#types';
7
+ import { Cursor } from '@dxos/link';
9
8
 
10
9
  /**
11
- * Reactive query for all routines connected to an object O, via two structural paths:
10
+ * Reactive query for all routines connected to an object O, via three structural paths:
12
11
  *
13
12
  * 1. **Trigger path** (two hops): O is referenced by a Trigger (via `input` or `spec.feed`), and that
14
13
  * Trigger is referenced by a Routine's `triggers` array.
15
- * 2. **Instructions path** (two hops): O is listed in an Instructions' `objects` context array, and those
14
+ * 2. **Cursor path** (three hops): a sync Trigger doesn't reference its synced target directly — its `binding`
15
+ * references an external-sync {@link Cursor}, and the Cursor's `spec.target` references O. So O is reached
16
+ * one hop further out (O ← Cursor ← Trigger ← Routine), keeping the target ref out of the operation input.
17
+ * 3. **Instructions path** (two hops): O is listed in an Instructions' `objects` context array, and those
16
18
  * Instructions are the Routine's action (`spec.instructions`). Both hops traverse `Ref` fields, so they are
17
19
  * fully queryable — no JavaScript parent-symbol traversal needed.
18
20
  *
@@ -25,7 +27,9 @@ export const connectedRoutinesQuery = (object: Obj.Unknown): Query.Query<Routine
25
27
  // Feed variant: O's feed ← Trigger.spec.feed ← Routine.triggers.
26
28
  // `.reference('feed')` is empty for objects without a feed prop, so this adds nothing for non-feed objects.
27
29
  const byFeed = Query.select(Filter.id(object.id)).reference('feed').referencedBy(Trigger.Trigger);
28
- const byTrigger = Query.all(byInput, byFeed).referencedBy(Routine.Routine, 'triggers');
30
+ // Cursor variant: O ← Cursor (spec.target) ← Trigger (input.binding) ← Routine.triggers.
31
+ const byCursor = Query.select(Filter.id(object.id)).referencedBy(Cursor.Cursor).referencedBy(Trigger.Trigger);
32
+ const byTrigger = Query.all(byInput, byFeed, byCursor).referencedBy(Routine.Routine, 'triggers');
29
33
 
30
34
  // Instructions path: O ← Instructions.objects ← Routine (via `spec.instructions`). The second hop drops the
31
35
  // property key: the instructions ref is nested in the `spec` union, and the reverse-ref index is structural,
@@ -47,7 +51,10 @@ export const routinesForObject = (object: Obj.Unknown, routines: Routine.Routine
47
51
  export const routineReferencesObject = (routine: Routine.Routine, object: Obj.Unknown): boolean =>
48
52
  referencesViaTrigger(routine, object) || referencesViaInstructions(routine, object);
49
53
 
50
- /** Trigger path: a trigger's `input` references O, or a feed trigger is bound to O's feed. */
54
+ /**
55
+ * Trigger path: a trigger's `input` references O (directly, or via a bound {@link Cursor} whose `spec.target`
56
+ * is O), or a feed trigger is bound to O's feed.
57
+ */
51
58
  const referencesViaTrigger = (routine: Routine.Routine, object: Obj.Unknown): boolean => {
52
59
  const objectFeedId = getFeedId(object);
53
60
  return routine.triggers.some((ref) => {
@@ -60,7 +67,10 @@ const referencesViaTrigger = (routine: Routine.Routine, object: Obj.Unknown): bo
60
67
  return true;
61
68
  }
62
69
 
63
- return trigger.input != null && referencesId(trigger.input, object.id);
70
+ if (trigger.input == null) {
71
+ return false;
72
+ }
73
+ return referencesId(trigger.input, object.id) || referencesViaCursor(trigger.input, object.id);
64
74
  });
65
75
  };
66
76
 
@@ -74,6 +84,21 @@ const getFeedId = (object: Obj.Unknown): string | undefined => {
74
84
  return Ref.isRef(feedRef) ? feedRef.target?.id : undefined;
75
85
  };
76
86
 
87
+ /** Recursively scan a value for a Cursor ref (e.g. a sync trigger's `binding`) that itself references O. */
88
+ const referencesViaCursor = (value: unknown, id: string): boolean => {
89
+ if (Ref.isRef(value)) {
90
+ const cursor = value.target;
91
+ return Obj.instanceOf(Cursor.Cursor, cursor) && referencesId(cursor.spec, id);
92
+ }
93
+ if (Array.isArray(value)) {
94
+ return value.some((entry) => referencesViaCursor(entry, id));
95
+ }
96
+ if (value && typeof value === 'object') {
97
+ return Object.values(value).some((entry) => referencesViaCursor(entry, id));
98
+ }
99
+ return false;
100
+ };
101
+
77
102
  /** Recursively scan a value for a Ref whose target is the given object id. */
78
103
  const referencesId = (value: unknown, id: string): boolean => {
79
104
  if (Ref.isRef(value)) {
@@ -0,0 +1,77 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import { Instructions, Operation, Routine, Runnable, Trigger } from '@dxos/compute';
8
+ import { Obj, Ref } from '@dxos/echo';
9
+
10
+ import { blank } from '../templates';
11
+ import { isRunInstructions, runInstructionsRef } from './run-instructions';
12
+ import { makeRoutine } from './wire';
13
+
14
+ describe('wire', () => {
15
+ test('makeRoutine produces a typed Routine', ({ expect }) => {
16
+ const routine = makeRoutine({ name: 'Test', triggers: [] });
17
+ expect(Routine.instanceOf(routine)).toBe(true);
18
+ expect(Obj.instanceOf(Routine.Routine, routine)).toBe(true);
19
+ expect(Obj.getLabel(routine)).toBe('Test');
20
+ expect(routine.triggers).toEqual([]);
21
+ expect(routine.spec).toBeUndefined();
22
+ });
23
+
24
+ test('Runnable seam is currently the Operation type', ({ expect }) => {
25
+ // MVP: Runnable === Operation. Widening this to a union is the documented next step.
26
+ expect(Runnable.Runnable).toBe(Operation.PersistentOperation);
27
+ });
28
+
29
+ test('makeRoutine wires an instructions action so a single add yields a runnable routine', ({ expect }) => {
30
+ const instructions = Instructions.make({ name: 'Body', text: 'do something' });
31
+ const trigger = Trigger.make({ spec: Trigger.specTimer('0 9 * * *') });
32
+ const routine = makeRoutine({ name: 'R', instructions, trigger });
33
+
34
+ // The routine owns the instructions, and the trigger dispatches RunInstructions with those instructions
35
+ // bound into the trigger input so no separate persistence step is needed.
36
+ expect(Routine.instructionsRef(routine)?.target?.id).toBe(instructions.id);
37
+ expect(routine.triggers[0]?.target?.id).toBe(trigger.id);
38
+ expect(isRunInstructions(trigger.runnable)).toBe(true);
39
+ const bound = trigger.input?.instructions;
40
+ expect(Ref.isRef(bound) ? bound.target?.id : undefined).toBe(instructions.id);
41
+ // A trigger with no explicit `remote` flag runs locally.
42
+ expect(trigger.remote).toBeUndefined();
43
+ });
44
+
45
+ test('makeRoutine preserves an explicit trigger remote override', ({ expect }) => {
46
+ const trigger = Trigger.make({ remote: true });
47
+ const routine = makeRoutine({ name: 'R', trigger });
48
+ expect(routine.triggers[0]?.target?.remote).toBe(true);
49
+ });
50
+
51
+ test('makeRoutine wires triggers supplied via the `triggers` array', ({ expect }) => {
52
+ const instructions = Instructions.make({ name: 'Body', text: 'do something' });
53
+ const trigger = Trigger.make({ spec: Trigger.specTimer('0 9 * * *') });
54
+ const routine = makeRoutine({ name: 'R', instructions, triggers: [Ref.make(trigger)] });
55
+ expect(isRunInstructions(trigger.runnable)).toBe(true);
56
+ const bound = trigger.input?.instructions;
57
+ expect(Ref.isRef(bound) ? bound.target?.id : undefined).toBe(instructions.id);
58
+ });
59
+
60
+ test('makeRoutine leaves pre-set trigger bindings alone when there is no action', ({ expect }) => {
61
+ const trigger = Trigger.make({});
62
+ const preset = runInstructionsRef();
63
+ Obj.update(trigger, (trigger) => {
64
+ trigger.runnable = preset;
65
+ });
66
+ makeRoutine({ name: 'R', trigger });
67
+ expect(trigger.runnable?.uri).toBe(preset.uri);
68
+ });
69
+
70
+ describe('blank template', () => {
71
+ test('is the default no-op template', ({ expect }) => {
72
+ expect(blank.id).toBe('org.dxos.routine.blank');
73
+ expect(blank.label).toBe('Blank');
74
+ expect(typeof blank.scaffold).toBe('function');
75
+ });
76
+ });
77
+ });