@agent-native/dispatch 0.6.1 → 0.8.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 (341) hide show
  1. package/README.md +38 -1
  2. package/dist/actions/apply-dream-proposal.d.ts +3 -0
  3. package/dist/actions/apply-dream-proposal.d.ts.map +1 -0
  4. package/dist/actions/apply-dream-proposal.js +11 -0
  5. package/dist/actions/apply-dream-proposal.js.map +1 -0
  6. package/dist/actions/create-dream-report.d.ts +3 -0
  7. package/dist/actions/create-dream-report.d.ts.map +1 -0
  8. package/dist/actions/create-dream-report.js +67 -0
  9. package/dist/actions/create-dream-report.js.map +1 -0
  10. package/dist/actions/create-pylon-ticket.d.ts +3 -0
  11. package/dist/actions/create-pylon-ticket.d.ts.map +1 -0
  12. package/dist/actions/create-pylon-ticket.js +94 -0
  13. package/dist/actions/create-pylon-ticket.js.map +1 -0
  14. package/dist/actions/create-vault-grant.js +1 -1
  15. package/dist/actions/create-vault-grant.js.map +1 -1
  16. package/dist/actions/create-vault-secret.d.ts.map +1 -1
  17. package/dist/actions/create-vault-secret.js +4 -3
  18. package/dist/actions/create-vault-secret.js.map +1 -1
  19. package/dist/actions/create-workspace-resource.js +3 -3
  20. package/dist/actions/create-workspace-resource.js.map +1 -1
  21. package/dist/actions/delete-workspace-resource.js +1 -1
  22. package/dist/actions/delete-workspace-resource.js.map +1 -1
  23. package/dist/actions/ensure-dream-job.d.ts +3 -0
  24. package/dist/actions/ensure-dream-job.d.ts.map +1 -0
  25. package/dist/actions/ensure-dream-job.js +73 -0
  26. package/dist/actions/ensure-dream-job.js.map +1 -0
  27. package/dist/actions/get-dream-settings.d.ts +3 -0
  28. package/dist/actions/get-dream-settings.d.ts.map +1 -0
  29. package/dist/actions/get-dream-settings.js +11 -0
  30. package/dist/actions/get-dream-settings.js.map +1 -0
  31. package/dist/actions/get-dream.d.ts +3 -0
  32. package/dist/actions/get-dream.d.ts.map +1 -0
  33. package/dist/actions/get-dream.js +13 -0
  34. package/dist/actions/get-dream.js.map +1 -0
  35. package/dist/actions/get-vault-access-settings.d.ts +3 -0
  36. package/dist/actions/get-vault-access-settings.d.ts.map +1 -0
  37. package/dist/actions/get-vault-access-settings.js +10 -0
  38. package/dist/actions/get-vault-access-settings.js.map +1 -0
  39. package/dist/actions/get-workspace-resource-effective-context.d.ts +3 -0
  40. package/dist/actions/get-workspace-resource-effective-context.d.ts.map +1 -0
  41. package/dist/actions/get-workspace-resource-effective-context.js +27 -0
  42. package/dist/actions/get-workspace-resource-effective-context.js.map +1 -0
  43. package/dist/actions/grant-vault-secrets-to-app.js +1 -1
  44. package/dist/actions/grant-vault-secrets-to-app.js.map +1 -1
  45. package/dist/actions/index.d.ts.map +1 -1
  46. package/dist/actions/index.js +38 -4
  47. package/dist/actions/index.js.map +1 -1
  48. package/dist/actions/list-dream-candidates.d.ts +3 -0
  49. package/dist/actions/list-dream-candidates.d.ts.map +1 -0
  50. package/dist/actions/list-dream-candidates.js +68 -0
  51. package/dist/actions/list-dream-candidates.js.map +1 -0
  52. package/dist/actions/list-dreams.d.ts +3 -0
  53. package/dist/actions/list-dreams.d.ts.map +1 -0
  54. package/dist/actions/list-dreams.js +17 -0
  55. package/dist/actions/list-dreams.js.map +1 -0
  56. package/dist/actions/list-integrations-catalog.js +1 -1
  57. package/dist/actions/list-integrations-catalog.js.map +1 -1
  58. package/dist/actions/list-vault-grants.js +1 -1
  59. package/dist/actions/list-vault-grants.js.map +1 -1
  60. package/dist/actions/list-workspace-apps.d.ts.map +1 -1
  61. package/dist/actions/list-workspace-apps.js +5 -1
  62. package/dist/actions/list-workspace-apps.js.map +1 -1
  63. package/dist/actions/list-workspace-resources-for-app.d.ts +3 -0
  64. package/dist/actions/list-workspace-resources-for-app.d.ts.map +1 -0
  65. package/dist/actions/list-workspace-resources-for-app.js +12 -0
  66. package/dist/actions/list-workspace-resources-for-app.js.map +1 -0
  67. package/dist/actions/list-workspace-resources.js +1 -1
  68. package/dist/actions/list-workspace-resources.js.map +1 -1
  69. package/dist/actions/navigate.d.ts +1 -0
  70. package/dist/actions/navigate.d.ts.map +1 -1
  71. package/dist/actions/navigate.js +2 -1
  72. package/dist/actions/navigate.js.map +1 -1
  73. package/dist/actions/preview-dream-proposal.d.ts +3 -0
  74. package/dist/actions/preview-dream-proposal.d.ts.map +1 -0
  75. package/dist/actions/preview-dream-proposal.js +13 -0
  76. package/dist/actions/preview-dream-proposal.js.map +1 -0
  77. package/dist/actions/preview-workspace-resource-change.d.ts +3 -0
  78. package/dist/actions/preview-workspace-resource-change.d.ts.map +1 -0
  79. package/dist/actions/preview-workspace-resource-change.js +24 -0
  80. package/dist/actions/preview-workspace-resource-change.js.map +1 -0
  81. package/dist/actions/reject-dream-proposal.d.ts +3 -0
  82. package/dist/actions/reject-dream-proposal.d.ts.map +1 -0
  83. package/dist/actions/reject-dream-proposal.js +12 -0
  84. package/dist/actions/reject-dream-proposal.js.map +1 -0
  85. package/dist/actions/restore-starter-workspace-resources.d.ts +3 -0
  86. package/dist/actions/restore-starter-workspace-resources.d.ts.map +1 -0
  87. package/dist/actions/restore-starter-workspace-resources.js +14 -0
  88. package/dist/actions/restore-starter-workspace-resources.js.map +1 -0
  89. package/dist/actions/send-code-agent-remote-command.d.ts +3 -0
  90. package/dist/actions/send-code-agent-remote-command.d.ts.map +1 -0
  91. package/dist/actions/send-code-agent-remote-command.js +53 -0
  92. package/dist/actions/send-code-agent-remote-command.js.map +1 -0
  93. package/dist/actions/set-dream-settings.d.ts +3 -0
  94. package/dist/actions/set-dream-settings.d.ts.map +1 -0
  95. package/dist/actions/set-dream-settings.js +41 -0
  96. package/dist/actions/set-dream-settings.js.map +1 -0
  97. package/dist/actions/set-vault-access-settings.d.ts +3 -0
  98. package/dist/actions/set-vault-access-settings.d.ts.map +1 -0
  99. package/dist/actions/set-vault-access-settings.js +13 -0
  100. package/dist/actions/set-vault-access-settings.js.map +1 -0
  101. package/dist/actions/start-workspace-app-creation.d.ts.map +1 -1
  102. package/dist/actions/start-workspace-app-creation.js +6 -0
  103. package/dist/actions/start-workspace-app-creation.js.map +1 -1
  104. package/dist/actions/sync-vault-to-app.js +1 -1
  105. package/dist/actions/sync-vault-to-app.js.map +1 -1
  106. package/dist/actions/update-workspace-app-metadata.d.ts +3 -0
  107. package/dist/actions/update-workspace-app-metadata.d.ts.map +1 -0
  108. package/dist/actions/update-workspace-app-metadata.js +30 -0
  109. package/dist/actions/update-workspace-app-metadata.js.map +1 -0
  110. package/dist/actions/update-workspace-resource.js +1 -1
  111. package/dist/actions/update-workspace-resource.js.map +1 -1
  112. package/dist/actions/view-screen.d.ts.map +1 -1
  113. package/dist/actions/view-screen.js +77 -4
  114. package/dist/actions/view-screen.js.map +1 -1
  115. package/dist/components/app-keys-popover.js +16 -5
  116. package/dist/components/app-keys-popover.js.map +1 -1
  117. package/dist/components/approval-value-block.d.ts +7 -0
  118. package/dist/components/approval-value-block.d.ts.map +1 -0
  119. package/dist/components/approval-value-block.js +22 -0
  120. package/dist/components/approval-value-block.js.map +1 -0
  121. package/dist/components/create-app-popover.d.ts.map +1 -1
  122. package/dist/components/create-app-popover.js +41 -16
  123. package/dist/components/create-app-popover.js.map +1 -1
  124. package/dist/components/dispatch-shell.d.ts +4 -4
  125. package/dist/components/dispatch-shell.d.ts.map +1 -1
  126. package/dist/components/dispatch-shell.js +6 -6
  127. package/dist/components/dispatch-shell.js.map +1 -1
  128. package/dist/components/layout/Layout.d.ts.map +1 -1
  129. package/dist/components/layout/Layout.js +18 -4
  130. package/dist/components/layout/Layout.js.map +1 -1
  131. package/dist/components/messaging-setup-panel.d.ts.map +1 -1
  132. package/dist/components/messaging-setup-panel.js +2 -2
  133. package/dist/components/messaging-setup-panel.js.map +1 -1
  134. package/dist/components/ui/chart.d.ts +1 -1
  135. package/dist/components/workspace-app-card.d.ts.map +1 -1
  136. package/dist/components/workspace-app-card.js +63 -3
  137. package/dist/components/workspace-app-card.js.map +1 -1
  138. package/dist/components/workspace-resource-effective-stack.d.ts +11 -0
  139. package/dist/components/workspace-resource-effective-stack.d.ts.map +1 -0
  140. package/dist/components/workspace-resource-effective-stack.js +59 -0
  141. package/dist/components/workspace-resource-effective-stack.js.map +1 -0
  142. package/dist/components/workspace-resource-impact-preview.d.ts +9 -0
  143. package/dist/components/workspace-resource-impact-preview.d.ts.map +1 -0
  144. package/dist/components/workspace-resource-impact-preview.js +39 -0
  145. package/dist/components/workspace-resource-impact-preview.js.map +1 -0
  146. package/dist/db/migrations.d.ts.map +1 -1
  147. package/dist/db/migrations.js +59 -0
  148. package/dist/db/migrations.js.map +1 -1
  149. package/dist/db/schema.d.ts +714 -0
  150. package/dist/db/schema.d.ts.map +1 -1
  151. package/dist/db/schema.js +44 -2
  152. package/dist/db/schema.js.map +1 -1
  153. package/dist/hooks/use-navigation-state.d.ts +3 -0
  154. package/dist/hooks/use-navigation-state.d.ts.map +1 -1
  155. package/dist/hooks/use-navigation-state.js +35 -8
  156. package/dist/hooks/use-navigation-state.js.map +1 -1
  157. package/dist/lib/catch-all-target.d.ts +2 -0
  158. package/dist/lib/catch-all-target.d.ts.map +1 -0
  159. package/dist/lib/catch-all-target.js +95 -0
  160. package/dist/lib/catch-all-target.js.map +1 -0
  161. package/dist/lib/utils.d.ts +2 -1
  162. package/dist/lib/utils.d.ts.map +1 -1
  163. package/dist/lib/utils.js +5 -1
  164. package/dist/lib/utils.js.map +1 -1
  165. package/dist/lib/workspace-apps.d.ts +9 -0
  166. package/dist/lib/workspace-apps.d.ts.map +1 -1
  167. package/dist/lib/workspace-apps.js.map +1 -1
  168. package/dist/routes/index.d.ts.map +1 -1
  169. package/dist/routes/index.js +1 -0
  170. package/dist/routes/index.js.map +1 -1
  171. package/dist/routes/pages/$appId.d.ts +2 -2
  172. package/dist/routes/pages/$appId.d.ts.map +1 -1
  173. package/dist/routes/pages/$appId.js +17 -8
  174. package/dist/routes/pages/$appId.js.map +1 -1
  175. package/dist/routes/pages/approval.d.ts.map +1 -1
  176. package/dist/routes/pages/approval.js +4 -1
  177. package/dist/routes/pages/approval.js.map +1 -1
  178. package/dist/routes/pages/approvals.js +1 -1
  179. package/dist/routes/pages/approvals.js.map +1 -1
  180. package/dist/routes/pages/dream-settings.d.ts +34 -0
  181. package/dist/routes/pages/dream-settings.d.ts.map +1 -0
  182. package/dist/routes/pages/dream-settings.js +68 -0
  183. package/dist/routes/pages/dream-settings.js.map +1 -0
  184. package/dist/routes/pages/dreams.d.ts +5 -0
  185. package/dist/routes/pages/dreams.d.ts.map +1 -0
  186. package/dist/routes/pages/dreams.js +435 -0
  187. package/dist/routes/pages/dreams.js.map +1 -0
  188. package/dist/routes/pages/integrations.d.ts.map +1 -1
  189. package/dist/routes/pages/integrations.js +20 -15
  190. package/dist/routes/pages/integrations.js.map +1 -1
  191. package/dist/routes/pages/new-app.js +1 -1
  192. package/dist/routes/pages/new-app.js.map +1 -1
  193. package/dist/routes/pages/overview.d.ts.map +1 -1
  194. package/dist/routes/pages/overview.js +5 -1
  195. package/dist/routes/pages/overview.js.map +1 -1
  196. package/dist/routes/pages/vault.d.ts.map +1 -1
  197. package/dist/routes/pages/vault.js +23 -5
  198. package/dist/routes/pages/vault.js.map +1 -1
  199. package/dist/routes/pages/workspace.d.ts.map +1 -1
  200. package/dist/routes/pages/workspace.js +187 -35
  201. package/dist/routes/pages/workspace.js.map +1 -1
  202. package/dist/server/lib/app-creation-store.d.ts +13 -0
  203. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  204. package/dist/server/lib/app-creation-store.js +298 -11
  205. package/dist/server/lib/app-creation-store.js.map +1 -1
  206. package/dist/server/lib/dispatch-integrations.d.ts +1 -1
  207. package/dist/server/lib/dispatch-integrations.d.ts.map +1 -1
  208. package/dist/server/lib/dispatch-integrations.js +9 -4
  209. package/dist/server/lib/dispatch-integrations.js.map +1 -1
  210. package/dist/server/lib/dispatch-remote-commands.d.ts +83 -0
  211. package/dist/server/lib/dispatch-remote-commands.d.ts.map +1 -0
  212. package/dist/server/lib/dispatch-remote-commands.js +256 -0
  213. package/dist/server/lib/dispatch-remote-commands.js.map +1 -0
  214. package/dist/server/lib/dispatch-store.d.ts +26 -0
  215. package/dist/server/lib/dispatch-store.d.ts.map +1 -1
  216. package/dist/server/lib/dispatch-store.js +17 -1
  217. package/dist/server/lib/dispatch-store.js.map +1 -1
  218. package/dist/server/lib/dreams-store.d.ts +398 -0
  219. package/dist/server/lib/dreams-store.d.ts.map +1 -0
  220. package/dist/server/lib/dreams-store.js +2330 -0
  221. package/dist/server/lib/dreams-store.js.map +1 -0
  222. package/dist/server/lib/env-config.d.ts.map +1 -1
  223. package/dist/server/lib/env-config.js +5 -0
  224. package/dist/server/lib/env-config.js.map +1 -1
  225. package/dist/server/lib/onboarding-steps.d.ts +12 -0
  226. package/dist/server/lib/onboarding-steps.d.ts.map +1 -0
  227. package/dist/server/lib/onboarding-steps.js +47 -0
  228. package/dist/server/lib/onboarding-steps.js.map +1 -0
  229. package/dist/server/lib/thread-debug-store.d.ts +2 -2
  230. package/dist/server/lib/vault-store.d.ts +55 -0
  231. package/dist/server/lib/vault-store.d.ts.map +1 -1
  232. package/dist/server/lib/vault-store.js +210 -41
  233. package/dist/server/lib/vault-store.js.map +1 -1
  234. package/dist/server/lib/workspace-resources-store.d.ts +181 -17
  235. package/dist/server/lib/workspace-resources-store.d.ts.map +1 -1
  236. package/dist/server/lib/workspace-resources-store.js +737 -108
  237. package/dist/server/lib/workspace-resources-store.js.map +1 -1
  238. package/dist/server/plugins/agent-chat.d.ts.map +1 -1
  239. package/dist/server/plugins/agent-chat.js +2 -1
  240. package/dist/server/plugins/agent-chat.js.map +1 -1
  241. package/dist/server/plugins/core-routes.d.ts.map +1 -1
  242. package/dist/server/plugins/core-routes.js +4 -0
  243. package/dist/server/plugins/core-routes.js.map +1 -1
  244. package/dist/server/plugins/integrations.js +2 -2
  245. package/dist/server/plugins/integrations.js.map +1 -1
  246. package/package.json +15 -11
  247. package/src/actions/apply-dream-proposal.ts +12 -0
  248. package/src/actions/create-dream-report.ts +76 -0
  249. package/src/actions/create-pylon-ticket.ts +109 -0
  250. package/src/actions/create-vault-grant.ts +1 -1
  251. package/src/actions/create-vault-secret.ts +4 -3
  252. package/src/actions/create-workspace-resource.ts +3 -3
  253. package/src/actions/delete-workspace-resource.ts +1 -1
  254. package/src/actions/ensure-dream-job.ts +76 -0
  255. package/src/actions/get-dream-settings.ts +12 -0
  256. package/src/actions/get-dream.ts +14 -0
  257. package/src/actions/get-vault-access-settings.ts +11 -0
  258. package/src/actions/get-workspace-resource-effective-context.ts +34 -0
  259. package/src/actions/grant-vault-secrets-to-app.ts +1 -1
  260. package/src/actions/index.spec.ts +26 -0
  261. package/src/actions/index.ts +39 -4
  262. package/src/actions/list-dream-candidates.ts +77 -0
  263. package/src/actions/list-dreams.ts +17 -0
  264. package/src/actions/list-integrations-catalog.ts +1 -1
  265. package/src/actions/list-vault-grants.ts +1 -1
  266. package/src/actions/list-workspace-apps.ts +5 -1
  267. package/src/actions/list-workspace-resources-for-app.ts +13 -0
  268. package/src/actions/list-workspace-resources.ts +1 -1
  269. package/src/actions/navigate.ts +2 -1
  270. package/src/actions/preview-dream-proposal.ts +14 -0
  271. package/src/actions/preview-workspace-resource-change.ts +25 -0
  272. package/src/actions/reject-dream-proposal.ts +12 -0
  273. package/src/actions/restore-starter-workspace-resources.ts +17 -0
  274. package/src/actions/send-code-agent-remote-command.ts +59 -0
  275. package/src/actions/set-dream-settings.spec.ts +81 -0
  276. package/src/actions/set-dream-settings.ts +44 -0
  277. package/src/actions/set-vault-access-settings.ts +16 -0
  278. package/src/actions/start-workspace-app-creation.ts +8 -0
  279. package/src/actions/sync-vault-to-app.ts +1 -1
  280. package/src/actions/update-workspace-app-metadata.ts +32 -0
  281. package/src/actions/update-workspace-resource.ts +1 -1
  282. package/src/actions/view-screen.ts +94 -3
  283. package/src/components/app-keys-popover.tsx +23 -7
  284. package/src/components/approval-value-block.spec.tsx +59 -0
  285. package/src/components/approval-value-block.tsx +33 -0
  286. package/src/components/create-app-popover.tsx +50 -16
  287. package/src/components/dispatch-shell.tsx +16 -15
  288. package/src/components/layout/Layout.tsx +19 -5
  289. package/src/components/messaging-setup-panel.tsx +54 -39
  290. package/src/components/workspace-app-card.tsx +268 -1
  291. package/src/components/workspace-resource-effective-stack.spec.tsx +125 -0
  292. package/src/components/workspace-resource-effective-stack.tsx +141 -0
  293. package/src/components/workspace-resource-impact-preview.spec.tsx +147 -0
  294. package/src/components/workspace-resource-impact-preview.tsx +116 -0
  295. package/src/db/migrations.ts +59 -0
  296. package/src/db/schema.ts +46 -2
  297. package/src/hooks/use-navigation-state.ts +34 -9
  298. package/src/lib/catch-all-target.spec.ts +218 -0
  299. package/src/lib/catch-all-target.ts +99 -0
  300. package/src/lib/utils.ts +6 -1
  301. package/src/lib/workspace-apps.ts +9 -0
  302. package/src/routes/index.ts +1 -0
  303. package/src/routes/pages/$appId.tsx +21 -8
  304. package/src/routes/pages/approval.tsx +14 -1
  305. package/src/routes/pages/approvals.tsx +1 -1
  306. package/src/routes/pages/dream-settings.spec.ts +130 -0
  307. package/src/routes/pages/dream-settings.ts +103 -0
  308. package/src/routes/pages/dreams.tsx +1828 -0
  309. package/src/routes/pages/integrations.tsx +57 -18
  310. package/src/routes/pages/new-app.tsx +1 -1
  311. package/src/routes/pages/overview.tsx +11 -3
  312. package/src/routes/pages/vault.tsx +76 -9
  313. package/src/routes/pages/workspace.tsx +577 -97
  314. package/src/server/lib/app-creation-store.spec.ts +61 -2
  315. package/src/server/lib/app-creation-store.ts +389 -13
  316. package/src/server/lib/dispatch-integrations.ts +10 -3
  317. package/src/server/lib/dispatch-remote-commands.spec.ts +167 -0
  318. package/src/server/lib/dispatch-remote-commands.ts +375 -0
  319. package/src/server/lib/dispatch-store.ts +37 -1
  320. package/src/server/lib/dreams-store.spec.ts +1492 -0
  321. package/src/server/lib/dreams-store.ts +3168 -0
  322. package/src/server/lib/env-config.ts +5 -0
  323. package/src/server/lib/onboarding-steps.ts +49 -0
  324. package/src/server/lib/vault-store.spec.ts +69 -0
  325. package/src/server/lib/vault-store.ts +266 -49
  326. package/src/server/lib/workspace-resource-approval-lifecycle.spec.ts +236 -0
  327. package/src/server/lib/workspace-resources-store.spec.ts +1106 -0
  328. package/src/server/lib/workspace-resources-store.ts +1001 -134
  329. package/src/server/plugins/agent-chat.ts +2 -1
  330. package/src/server/plugins/core-routes.ts +5 -0
  331. package/src/server/plugins/integrations.ts +2 -2
  332. package/dist/actions/sync-workspace-resources-to-all.d.ts +0 -3
  333. package/dist/actions/sync-workspace-resources-to-all.d.ts.map +0 -1
  334. package/dist/actions/sync-workspace-resources-to-all.js +0 -9
  335. package/dist/actions/sync-workspace-resources-to-all.js.map +0 -1
  336. package/dist/actions/sync-workspace-resources-to-app.d.ts +0 -3
  337. package/dist/actions/sync-workspace-resources-to-app.d.ts.map +0 -1
  338. package/dist/actions/sync-workspace-resources-to-app.js +0 -11
  339. package/dist/actions/sync-workspace-resources-to-app.js.map +0 -1
  340. package/src/actions/sync-workspace-resources-to-all.ts +0 -10
  341. package/src/actions/sync-workspace-resources-to-app.ts +0 -12
@@ -1,19 +1,25 @@
1
- import { useState } from "react";
1
+ import { useEffect, useState, type ReactNode } from "react";
2
2
  import { useActionMutation, useActionQuery } from "@agent-native/core/client";
3
3
  import { toast } from "sonner";
4
4
  import {
5
+ IconAlertCircle,
5
6
  IconBook,
6
7
  IconChevronDown,
7
8
  IconChevronRight,
9
+ IconCircleCheck,
8
10
  IconCode,
11
+ IconEdit,
9
12
  IconFileText,
10
13
  IconPlus,
11
- IconRefresh,
12
14
  IconTrash,
13
15
  IconUser,
14
16
  IconX,
15
17
  } from "@tabler/icons-react";
16
18
  import { DispatchShell } from "@/components/dispatch-shell";
19
+ import {
20
+ ImpactPreview,
21
+ workspaceResourceMutationMessage,
22
+ } from "@/components/workspace-resource-impact-preview";
17
23
  import {
18
24
  AlertDialog,
19
25
  AlertDialogAction,
@@ -58,31 +64,252 @@ const KIND_CONFIG = {
58
64
  label: "Skill",
59
65
  icon: IconCode,
60
66
  pathPrefix: "skills/",
61
- description: "Agent skills — detailed guidance for patterns and workflows",
67
+ description:
68
+ "Agent skills - on-demand guidance available across workspace apps",
62
69
  },
63
70
  instruction: {
64
71
  label: "Instruction",
65
72
  icon: IconBook,
66
- pathPrefix: "",
67
- description:
68
- "Agent instructions — operational rules and behavioral guidance",
73
+ pathPrefix: "instructions/",
74
+ description: "Global instructions - guardrails loaded by every app agent",
69
75
  },
70
76
  agent: {
71
77
  label: "Agent",
72
78
  icon: IconUser,
73
79
  pathPrefix: "agents/",
74
80
  description:
75
- "Reusable agent profiles specialist agents shared across apps",
81
+ "Reusable agent profiles - specialist agents shared across apps",
76
82
  },
77
83
  knowledge: {
78
84
  label: "Knowledge",
79
85
  icon: IconFileText,
80
86
  pathPrefix: "context/",
81
87
  description:
82
- "Knowledge packs reusable GTM, product, and domain context for apps",
88
+ "Reference resources - brand, positioning, persona, and domain context",
83
89
  },
84
90
  } as const;
85
91
 
92
+ const STARTER_GLOBAL_CONTEXT = [
93
+ {
94
+ path: "context/company.md",
95
+ label: "Company",
96
+ kind: "knowledge",
97
+ description: "Company facts, ICP, products, and canonical links",
98
+ },
99
+ {
100
+ path: "context/brand.md",
101
+ label: "Brand",
102
+ kind: "knowledge",
103
+ description: "Voice, visual identity, naming, and style rules",
104
+ },
105
+ {
106
+ path: "context/messaging.md",
107
+ label: "Messaging",
108
+ kind: "knowledge",
109
+ description: "Positioning, value props, proof points, and objections",
110
+ },
111
+ {
112
+ path: "instructions/guardrails.md",
113
+ label: "Guardrails",
114
+ kind: "instruction",
115
+ description: "Always-on rules loaded by every app agent",
116
+ },
117
+ {
118
+ path: "skills/company-voice/SKILL.md",
119
+ label: "Company Voice",
120
+ kind: "skill",
121
+ description: "On-demand guidance for customer-facing writing",
122
+ },
123
+ ] as const;
124
+
125
+ function resourceSlug(value: string): string {
126
+ return value
127
+ .toLowerCase()
128
+ .replace(/[^a-z0-9]+/g, "-")
129
+ .replace(/^-+|-+$/g, "");
130
+ }
131
+
132
+ function defaultResourcePath(kind: string, name: string): string {
133
+ const slug = resourceSlug(name) || "example";
134
+ if (kind === "skill") return `skills/${slug}/SKILL.md`;
135
+ if (kind === "instruction") return `instructions/${slug}.md`;
136
+ if (kind === "agent") return `agents/${slug}.md`;
137
+ if (kind === "knowledge") return `context/${slug}.md`;
138
+ return `${slug}.md`;
139
+ }
140
+
141
+ function isAutoLoadedInstruction(resource: any): boolean {
142
+ return (
143
+ resource.kind === "instruction" &&
144
+ (resource.path === "AGENTS.md" ||
145
+ String(resource.path).startsWith("instructions/"))
146
+ );
147
+ }
148
+
149
+ function formatTimestamp(value?: number | null): string {
150
+ if (!value) return "Not present";
151
+ return new Date(value).toLocaleString();
152
+ }
153
+
154
+ function availabilityLabel(value?: string): string {
155
+ switch (value) {
156
+ case "all-apps":
157
+ return "Inherited by all apps";
158
+ case "selected-granted":
159
+ return "Granted to selected app";
160
+ case "selected-not-granted":
161
+ return "Not granted to this app";
162
+ case "selected-no-app":
163
+ return "Select an app to check grant";
164
+ case "path-not-managed":
165
+ return "Path is not a Dispatch resource";
166
+ default:
167
+ return "Checking availability";
168
+ }
169
+ }
170
+
171
+ function layerState(layer: any): {
172
+ label: string;
173
+ className: string;
174
+ } {
175
+ if (layer.effective) {
176
+ return {
177
+ label: "Active",
178
+ className: "border-green-500/30 bg-green-500/10 text-green-700",
179
+ };
180
+ }
181
+ if (layer.overridden) {
182
+ return {
183
+ label: "Overridden",
184
+ className: "border-amber-500/30 bg-amber-500/10 text-amber-700",
185
+ };
186
+ }
187
+ return {
188
+ label: "Missing",
189
+ className: "text-muted-foreground",
190
+ };
191
+ }
192
+
193
+ function EditResourceDialog({
194
+ resource,
195
+ trigger,
196
+ }: {
197
+ resource: any;
198
+ trigger?: ReactNode;
199
+ }) {
200
+ const [open, setOpen] = useState(false);
201
+ const [name, setName] = useState(resource.name || "");
202
+ const [description, setDescription] = useState(resource.description || "");
203
+ const [content, setContent] = useState(resource.content || "");
204
+ const [scope, setScope] = useState(resource.scope || "all");
205
+
206
+ useEffect(() => {
207
+ if (!open) return;
208
+ setName(resource.name || "");
209
+ setDescription(resource.description || "");
210
+ setContent(resource.content || "");
211
+ setScope(resource.scope || "all");
212
+ }, [open, resource]);
213
+
214
+ const update = useActionMutation("update-workspace-resource", {
215
+ onSuccess: (result: any) => {
216
+ toast.success(
217
+ workspaceResourceMutationMessage(result, "Resource updated"),
218
+ );
219
+ setOpen(false);
220
+ },
221
+ onError: (err) => toast.error(String(err)),
222
+ });
223
+
224
+ return (
225
+ <Dialog open={open} onOpenChange={setOpen}>
226
+ <DialogTrigger asChild>
227
+ {trigger || (
228
+ <Button variant="outline" size="sm">
229
+ <IconEdit size={14} className="mr-1.5" />
230
+ Edit
231
+ </Button>
232
+ )}
233
+ </DialogTrigger>
234
+ <DialogContent className="max-w-2xl">
235
+ <DialogHeader>
236
+ <DialogTitle>Edit workspace resource</DialogTitle>
237
+ <DialogDescription>
238
+ Updates apply immediately anywhere this workspace resource is
239
+ inherited. App shared or personal resources can override it locally.
240
+ </DialogDescription>
241
+ </DialogHeader>
242
+ <div className="space-y-4 py-2">
243
+ <div className="grid grid-cols-2 gap-4">
244
+ <div className="space-y-2">
245
+ <Label>Name</Label>
246
+ <Input value={name} onChange={(e) => setName(e.target.value)} />
247
+ </div>
248
+ <div className="space-y-2">
249
+ <Label>Scope</Label>
250
+ <Select value={scope} onValueChange={setScope}>
251
+ <SelectTrigger>
252
+ <SelectValue />
253
+ </SelectTrigger>
254
+ <SelectContent>
255
+ <SelectItem value="all">All apps</SelectItem>
256
+ <SelectItem value="selected">Selected apps only</SelectItem>
257
+ </SelectContent>
258
+ </Select>
259
+ </div>
260
+ </div>
261
+ <div className="space-y-2">
262
+ <Label>Path</Label>
263
+ <Input
264
+ value={resource.path}
265
+ disabled
266
+ className="font-mono text-sm"
267
+ />
268
+ </div>
269
+ <div className="space-y-2">
270
+ <Label>Description</Label>
271
+ <Input
272
+ value={description}
273
+ onChange={(e) => setDescription(e.target.value)}
274
+ />
275
+ </div>
276
+ <div className="space-y-2">
277
+ <Label>Content</Label>
278
+ <Textarea
279
+ value={content}
280
+ onChange={(e) => setContent(e.target.value)}
281
+ rows={14}
282
+ className="font-mono text-sm"
283
+ />
284
+ </div>
285
+ <ImpactPreview
286
+ operation="update"
287
+ resourceId={resource.id}
288
+ scope={scope as "all" | "selected"}
289
+ enabled={open}
290
+ />
291
+ </div>
292
+ <DialogFooter>
293
+ <Button
294
+ onClick={() =>
295
+ update.mutate({
296
+ id: resource.id,
297
+ name,
298
+ description,
299
+ content,
300
+ scope: scope as "all" | "selected",
301
+ })
302
+ }
303
+ disabled={!name.trim() || update.isPending}
304
+ >
305
+ {update.isPending ? "Saving..." : "Save changes"}
306
+ </Button>
307
+ </DialogFooter>
308
+ </DialogContent>
309
+ </Dialog>
310
+ );
311
+ }
312
+
86
313
  function AddResourceDialog() {
87
314
  const [open, setOpen] = useState(false);
88
315
  const [kind, setKind] = useState<string>("skill");
@@ -93,8 +320,10 @@ function AddResourceDialog() {
93
320
  const [scope, setScope] = useState<string>("all");
94
321
 
95
322
  const create = useActionMutation("create-workspace-resource", {
96
- onSuccess: () => {
97
- toast.success("Resource created");
323
+ onSuccess: (result: any) => {
324
+ toast.success(
325
+ workspaceResourceMutationMessage(result, "Resource created"),
326
+ );
98
327
  setOpen(false);
99
328
  setKind("skill");
100
329
  setName("");
@@ -106,8 +335,6 @@ function AddResourceDialog() {
106
335
  onError: (err) => toast.error(String(err)),
107
336
  });
108
337
 
109
- const kindInfo = KIND_CONFIG[kind as keyof typeof KIND_CONFIG];
110
-
111
338
  return (
112
339
  <Dialog open={open} onOpenChange={setOpen}>
113
340
  <DialogTrigger asChild>
@@ -120,8 +347,8 @@ function AddResourceDialog() {
120
347
  <DialogHeader>
121
348
  <DialogTitle>Add workspace resource</DialogTitle>
122
349
  <DialogDescription>
123
- Create a skill, instruction, or agent profile that can be shared
124
- across workspace apps.
350
+ Create a skill, instruction, agent profile, or reference resource
351
+ that can be shared across workspace apps.
125
352
  </DialogDescription>
126
353
  </DialogHeader>
127
354
  <div className="space-y-4 py-2">
@@ -172,14 +399,15 @@ function AddResourceDialog() {
172
399
  <div className="space-y-2">
173
400
  <Label>Path</Label>
174
401
  <Input
175
- placeholder={`${kindInfo?.pathPrefix || ""}${name.toLowerCase().replace(/\s+/g, "-") || "example"}.md`}
402
+ placeholder={defaultResourcePath(kind, name)}
176
403
  value={path}
177
404
  onChange={(e) => setPath(e.target.value)}
178
405
  className="font-mono text-sm"
179
406
  />
180
407
  <p className="text-xs text-muted-foreground">
181
- Resource path in target apps. Skills go in skills/, agents in
182
- agents/, knowledge packs in context/.
408
+ Skills use skills/name/SKILL.md. Guardrails in AGENTS.md or
409
+ instructions/ auto-load in app chat. Reference resources in
410
+ context/ are indexed so agents can read them when relevant.
183
411
  </p>
184
412
  </div>
185
413
  <div className="space-y-2">
@@ -200,7 +428,7 @@ function AddResourceDialog() {
200
428
  ? "---\nname: Research Specialist\ndescription: Handles research tasks\n---\n\n# Instructions\n\n..."
201
429
  : kind === "knowledge"
202
430
  ? "# Core GTM Messaging\n\n## Positioning\n\n## ICP\n\n## Proof points\n\n## Source\n\n"
203
- : "# Instructions\n\nBehavioral rules and guidance for agents across apps..."
431
+ : "# Instructions\n\nAlways-on guardrails for agents across apps..."
204
432
  }
205
433
  value={content}
206
434
  onChange={(e) => setContent(e.target.value)}
@@ -208,6 +436,12 @@ function AddResourceDialog() {
208
436
  className="font-mono text-sm"
209
437
  />
210
438
  </div>
439
+ <ImpactPreview
440
+ operation="create"
441
+ path={path || defaultResourcePath(kind, name)}
442
+ scope={scope as "all" | "selected"}
443
+ enabled={open && Boolean(name.trim())}
444
+ />
211
445
  </div>
212
446
  <DialogFooter>
213
447
  <Button
@@ -216,9 +450,7 @@ function AddResourceDialog() {
216
450
  kind: kind as "skill" | "instruction" | "agent" | "knowledge",
217
451
  name,
218
452
  description: description || undefined,
219
- path:
220
- path ||
221
- `${kindInfo?.pathPrefix || ""}${name.toLowerCase().replace(/\s+/g, "-")}.md`,
453
+ path: path || defaultResourcePath(kind, name),
222
454
  content,
223
455
  scope: scope as "all" | "selected",
224
456
  })
@@ -300,29 +532,159 @@ function GrantDialog({
300
532
  );
301
533
  }
302
534
 
535
+ function EffectiveContextPreview({ resource }: { resource: any }) {
536
+ const [appId, setAppId] = useState("__any__");
537
+ const [userEmail, setUserEmail] = useState("");
538
+ const selectedAppId = appId === "__any__" ? undefined : appId;
539
+ const normalizedUserEmail = userEmail.trim() || undefined;
540
+ const { data: apps } = useActionQuery("list-workspace-apps", {
541
+ includeAgentCards: false,
542
+ });
543
+ const { data: context, isLoading } = useActionQuery(
544
+ "get-workspace-resource-effective-context",
545
+ {
546
+ resourceId: resource.id,
547
+ appId: selectedAppId,
548
+ userEmail: normalizedUserEmail,
549
+ },
550
+ );
551
+
552
+ const visibleApps = ((apps || []) as any[]).filter(
553
+ (app) => !app.isDispatch && app.status !== "pending",
554
+ );
555
+ const layers = ((context as any)?.layers || []) as any[];
556
+ const active = (context as any)?.effectiveResource;
557
+ const availability = (context as any)?.availability;
558
+
559
+ return (
560
+ <div className="rounded-lg border bg-background p-3">
561
+ <div className="flex flex-wrap items-start justify-between gap-3">
562
+ <div className="min-w-0">
563
+ <h4 className="text-xs font-semibold uppercase text-muted-foreground">
564
+ Effective in app
565
+ </h4>
566
+ <p className="mt-1 text-xs leading-relaxed text-muted-foreground">
567
+ Preview the runtime stack for this path: workspace default,
568
+ organization/app override, then personal override.
569
+ </p>
570
+ </div>
571
+ <Badge variant="outline">{availabilityLabel(availability)}</Badge>
572
+ </div>
573
+
574
+ <div className="mt-3 grid gap-3 md:grid-cols-2">
575
+ <div className="space-y-2">
576
+ <Label htmlFor={`resource-app-${resource.id}`}>App</Label>
577
+ <Select value={appId} onValueChange={setAppId}>
578
+ <SelectTrigger id={`resource-app-${resource.id}`}>
579
+ <SelectValue />
580
+ </SelectTrigger>
581
+ <SelectContent>
582
+ <SelectItem value="__any__">Any app</SelectItem>
583
+ {visibleApps.map((app) => (
584
+ <SelectItem key={app.id} value={app.id}>
585
+ {app.name}
586
+ </SelectItem>
587
+ ))}
588
+ </SelectContent>
589
+ </Select>
590
+ </div>
591
+ <div className="space-y-2">
592
+ <Label htmlFor={`resource-user-${resource.id}`}>User email</Label>
593
+ <Input
594
+ id={`resource-user-${resource.id}`}
595
+ value={userEmail}
596
+ onChange={(event) => setUserEmail(event.target.value)}
597
+ placeholder="Current Dispatch user"
598
+ />
599
+ </div>
600
+ </div>
601
+
602
+ {resource.scope === "selected" ? (
603
+ <div className="mt-3 rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs leading-relaxed text-amber-800 dark:text-amber-200">
604
+ Selected resources are app-specific exceptions. Use All apps for
605
+ company-wide context that should be inherited everywhere without copy
606
+ or sync.
607
+ </div>
608
+ ) : null}
609
+
610
+ {isLoading ? (
611
+ <div className="mt-3 grid gap-2 md:grid-cols-3">
612
+ {Array.from({ length: 3 }).map((_, index) => (
613
+ <Skeleton key={index} className="h-28 rounded-lg" />
614
+ ))}
615
+ </div>
616
+ ) : (
617
+ <div className="mt-3 grid gap-2 md:grid-cols-3">
618
+ {layers.map((layer) => {
619
+ const state = layerState(layer);
620
+ return (
621
+ <div key={layer.scope} className="rounded-lg border p-3">
622
+ <div className="flex items-start justify-between gap-2">
623
+ <span className="text-sm font-medium text-foreground">
624
+ {layer.label}
625
+ </span>
626
+ <Badge variant="outline" className={state.className}>
627
+ {state.label}
628
+ </Badge>
629
+ </div>
630
+ <div className="mt-2 truncate font-mono text-[11px] text-muted-foreground">
631
+ {layer.owner}
632
+ </div>
633
+ {layer.resource ? (
634
+ <div className="mt-2 space-y-1 text-xs text-muted-foreground">
635
+ <div className="truncate font-mono">
636
+ {layer.resource.path}
637
+ </div>
638
+ <div>{formatTimestamp(layer.resource.updatedAt)}</div>
639
+ </div>
640
+ ) : (
641
+ <p className="mt-2 text-xs text-muted-foreground">
642
+ No resource exists at this layer.
643
+ </p>
644
+ )}
645
+ </div>
646
+ );
647
+ })}
648
+ </div>
649
+ )}
650
+
651
+ <div className="mt-3 rounded-md bg-muted/40 px-3 py-2 text-xs text-muted-foreground">
652
+ {active ? (
653
+ <>
654
+ Active file:{" "}
655
+ <span className="font-mono text-foreground">
656
+ {active.owner}/{active.path}
657
+ </span>
658
+ </>
659
+ ) : (
660
+ "No active resource exists for this path yet."
661
+ )}
662
+ </div>
663
+ </div>
664
+ );
665
+ }
666
+
303
667
  function ResourceRow({ resource, grants }: { resource: any; grants: any[] }) {
304
668
  const [expanded, setExpanded] = useState(false);
305
669
 
306
670
  const deleteResource = useActionMutation("delete-workspace-resource", {
307
- onSuccess: () => toast.success("Resource deleted"),
671
+ onSuccess: (result: any) =>
672
+ toast.success(
673
+ workspaceResourceMutationMessage(result, "Resource deleted"),
674
+ ),
308
675
  onError: (err) => toast.error(String(err)),
309
676
  });
310
677
  const revokeGrant = useActionMutation("revoke-workspace-resource-grant", {
311
678
  onSuccess: () => toast.success("Grant revoked"),
312
679
  onError: (err) => toast.error(String(err)),
313
680
  });
314
- const syncToApp = useActionMutation("sync-workspace-resources-to-app", {
315
- onSuccess: (data: any) =>
316
- toast.success(`Synced ${data.synced} resource(s) to ${data.appId}`),
317
- onError: (err) => toast.error(String(err)),
318
- });
319
681
 
320
682
  const kindInfo = KIND_CONFIG[resource.kind as keyof typeof KIND_CONFIG];
321
683
  const KindIcon = kindInfo?.icon || IconCode;
322
684
  const activeGrants = grants.filter((g) => g.status === "active");
323
685
 
324
686
  return (
325
- <div className="rounded-xl border bg-card">
687
+ <div className="rounded-lg border bg-card">
326
688
  <button
327
689
  type="button"
328
690
  className="flex w-full items-center gap-3 px-4 py-3 text-left cursor-pointer"
@@ -352,6 +714,11 @@ function ResourceRow({ resource, grants }: { resource: any; grants: any[] }) {
352
714
  >
353
715
  {resource.scope === "all" ? "All apps" : "Selected"}
354
716
  </Badge>
717
+ {isAutoLoadedInstruction(resource) && (
718
+ <Badge variant="outline" className="text-xs">
719
+ Auto-loaded
720
+ </Badge>
721
+ )}
355
722
  </div>
356
723
  <div className="mt-0.5 font-mono text-xs text-muted-foreground">
357
724
  {resource.path}
@@ -381,6 +748,8 @@ function ResourceRow({ resource, grants }: { resource: any; grants: any[] }) {
381
748
  </pre>
382
749
  </div>
383
750
 
751
+ <EffectiveContextPreview resource={resource} />
752
+
384
753
  {resource.scope === "selected" && (
385
754
  <div className="space-y-2">
386
755
  <div className="flex items-center justify-between">
@@ -404,22 +773,10 @@ function ResourceRow({ resource, grants }: { resource: any; grants: any[] }) {
404
773
  {grant.appId}
405
774
  </span>
406
775
  <span className="ml-2 text-xs text-muted-foreground">
407
- {grant.syncedAt
408
- ? `synced ${new Date(grant.syncedAt).toLocaleString()}`
409
- : "not synced"}
776
+ selected grant
410
777
  </span>
411
778
  </div>
412
779
  <div className="flex gap-1.5">
413
- <Button
414
- variant="ghost"
415
- size="sm"
416
- onClick={() =>
417
- syncToApp.mutate({ appId: grant.appId })
418
- }
419
- disabled={syncToApp.isPending}
420
- >
421
- <IconRefresh size={14} />
422
- </Button>
423
780
  <Button
424
781
  variant="ghost"
425
782
  size="sm"
@@ -447,36 +804,44 @@ function ResourceRow({ resource, grants }: { resource: any; grants: any[] }) {
447
804
  Created by {resource.createdBy} ·{" "}
448
805
  {new Date(resource.createdAt).toLocaleString()}
449
806
  </div>
450
- <AlertDialog>
451
- <AlertDialogTrigger asChild>
452
- <Button
453
- variant="destructive"
454
- size="sm"
455
- disabled={deleteResource.isPending}
456
- >
457
- <IconTrash size={14} className="mr-1" />
458
- Delete
459
- </Button>
460
- </AlertDialogTrigger>
461
- <AlertDialogContent>
462
- <AlertDialogHeader>
463
- <AlertDialogTitle>Delete this resource?</AlertDialogTitle>
464
- <AlertDialogDescription>
465
- Removing "{resource.name}" revokes all of its grants. Apps
466
- that depended on this resource will lose access on the next
467
- sync. This cannot be undone.
468
- </AlertDialogDescription>
469
- </AlertDialogHeader>
470
- <AlertDialogFooter>
471
- <AlertDialogCancel>Cancel</AlertDialogCancel>
472
- <AlertDialogAction
473
- onClick={() => deleteResource.mutate({ id: resource.id })}
807
+ <div className="flex gap-2">
808
+ <EditResourceDialog resource={resource} />
809
+ <AlertDialog>
810
+ <AlertDialogTrigger asChild>
811
+ <Button
812
+ variant="destructive"
813
+ size="sm"
814
+ disabled={deleteResource.isPending}
474
815
  >
475
- Delete resource
476
- </AlertDialogAction>
477
- </AlertDialogFooter>
478
- </AlertDialogContent>
479
- </AlertDialog>
816
+ <IconTrash size={14} className="mr-1" />
817
+ Delete
818
+ </Button>
819
+ </AlertDialogTrigger>
820
+ <AlertDialogContent>
821
+ <AlertDialogHeader>
822
+ <AlertDialogTitle>Delete this resource?</AlertDialogTitle>
823
+ <AlertDialogDescription>
824
+ Removing "{resource.name}" revokes all of its grants and
825
+ removes inherited workspace access immediately. This
826
+ cannot be undone.
827
+ </AlertDialogDescription>
828
+ </AlertDialogHeader>
829
+ <ImpactPreview
830
+ operation="delete"
831
+ resourceId={resource.id}
832
+ enabled
833
+ />
834
+ <AlertDialogFooter>
835
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
836
+ <AlertDialogAction
837
+ onClick={() => deleteResource.mutate({ id: resource.id })}
838
+ >
839
+ Delete resource
840
+ </AlertDialogAction>
841
+ </AlertDialogFooter>
842
+ </AlertDialogContent>
843
+ </AlertDialog>
844
+ </div>
480
845
  </div>
481
846
  </div>
482
847
  )}
@@ -484,6 +849,143 @@ function ResourceRow({ resource, grants }: { resource: any; grants: any[] }) {
484
849
  );
485
850
  }
486
851
 
852
+ function GlobalContextSection({ resources }: { resources: any[] }) {
853
+ const byPath = new Map(
854
+ resources.map((resource) => [resource.path, resource]),
855
+ );
856
+ const missingPaths = STARTER_GLOBAL_CONTEXT.filter(
857
+ (item) => !byPath.has(item.path),
858
+ ).map((item) => item.path);
859
+ const presentCount = STARTER_GLOBAL_CONTEXT.length - missingPaths.length;
860
+ const restoreStarter = useActionMutation(
861
+ "restore-starter-workspace-resources",
862
+ {
863
+ onSuccess: (result: any) => {
864
+ const restored = result?.restored?.length ?? 0;
865
+ const existing = result?.existing?.length ?? 0;
866
+ toast.success(
867
+ restored > 0
868
+ ? `Restored ${restored} starter resource${restored === 1 ? "" : "s"}`
869
+ : `Starter resources already present (${existing})`,
870
+ );
871
+ },
872
+ onError: (err) => toast.error(String(err)),
873
+ },
874
+ );
875
+
876
+ return (
877
+ <section className="space-y-3">
878
+ <div className="flex items-center justify-between gap-3">
879
+ <div>
880
+ <h2 className="text-sm font-semibold text-foreground">
881
+ Global context
882
+ </h2>
883
+ <p className="mt-1 text-xs text-muted-foreground">
884
+ Starter resources every workspace can use for company facts, brand,
885
+ messaging, guardrails, and voice.
886
+ </p>
887
+ <p className="mt-1 text-xs text-muted-foreground">
888
+ All-app resources live once at workspace scope and are inherited by
889
+ every app agent at runtime.
890
+ </p>
891
+ </div>
892
+ <div className="flex shrink-0 items-center gap-2">
893
+ {missingPaths.length > 0 ? (
894
+ <Button
895
+ size="sm"
896
+ variant="outline"
897
+ onClick={() => restoreStarter.mutate({ paths: missingPaths })}
898
+ disabled={restoreStarter.isPending}
899
+ >
900
+ <IconPlus size={14} className="mr-1.5" />
901
+ {restoreStarter.isPending ? "Restoring..." : "Restore missing"}
902
+ </Button>
903
+ ) : null}
904
+ <Badge variant="outline">
905
+ {presentCount}/{STARTER_GLOBAL_CONTEXT.length} ready
906
+ </Badge>
907
+ </div>
908
+ </div>
909
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-5">
910
+ {STARTER_GLOBAL_CONTEXT.map((item) => {
911
+ const resource = byPath.get(item.path);
912
+ const exists = !!resource;
913
+ const global = resource?.scope === "all";
914
+ return (
915
+ <div key={item.path} className="rounded-lg border bg-card p-4">
916
+ <div className="flex items-start justify-between gap-3">
917
+ <div className="min-w-0">
918
+ <div className="flex items-center gap-2">
919
+ {exists ? (
920
+ <IconCircleCheck
921
+ size={15}
922
+ className="shrink-0 text-green-600 dark:text-green-400"
923
+ />
924
+ ) : (
925
+ <IconAlertCircle
926
+ size={15}
927
+ className="shrink-0 text-amber-600 dark:text-amber-400"
928
+ />
929
+ )}
930
+ <h3 className="truncate text-sm font-medium text-foreground">
931
+ {item.label}
932
+ </h3>
933
+ </div>
934
+ <p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
935
+ {item.description}
936
+ </p>
937
+ </div>
938
+ {resource ? (
939
+ <EditResourceDialog
940
+ resource={resource}
941
+ trigger={
942
+ <Button variant="ghost" size="sm" className="h-7 px-2">
943
+ <span className="sr-only">Edit {item.label}</span>
944
+ <IconEdit size={14} />
945
+ </Button>
946
+ }
947
+ />
948
+ ) : (
949
+ <Button
950
+ variant="ghost"
951
+ size="sm"
952
+ className="h-7 px-2"
953
+ onClick={() =>
954
+ restoreStarter.mutate({ paths: [item.path] })
955
+ }
956
+ disabled={restoreStarter.isPending}
957
+ aria-label={`Restore ${item.label}`}
958
+ >
959
+ <IconPlus size={14} />
960
+ </Button>
961
+ )}
962
+ </div>
963
+ <div className="mt-3 space-y-2">
964
+ <div className="truncate font-mono text-[11px] text-muted-foreground">
965
+ {item.path}
966
+ </div>
967
+ <div className="flex flex-wrap gap-1.5">
968
+ <Badge variant={exists ? "secondary" : "outline"}>
969
+ {exists ? "Present" : "Missing"}
970
+ </Badge>
971
+ {exists ? (
972
+ <Badge variant="outline">
973
+ {global ? "All apps" : "Selected"}
974
+ </Badge>
975
+ ) : null}
976
+ {resource && isAutoLoadedInstruction(resource) ? (
977
+ <Badge variant="outline">Auto-loaded</Badge>
978
+ ) : null}
979
+ </div>
980
+ </div>
981
+ </div>
982
+ );
983
+ })}
984
+ </div>
985
+ </section>
986
+ );
987
+ }
988
+
487
989
  export default function WorkspaceRoute() {
488
990
  const { data: resources, isLoading } = useActionQuery(
489
991
  "list-workspace-resources",
@@ -491,19 +993,6 @@ export default function WorkspaceRoute() {
491
993
  );
492
994
  const { data: grants } = useActionQuery("list-workspace-resource-grants", {});
493
995
 
494
- const syncAll = useActionMutation("sync-workspace-resources-to-all", {
495
- onSuccess: (data: any) => {
496
- const total = (data || []).reduce(
497
- (sum: number, r: any) => sum + r.synced,
498
- 0,
499
- );
500
- toast.success(
501
- `Synced resources to ${data?.length || 0} apps (${total} total pushes)`,
502
- );
503
- },
504
- onError: (err) => toast.error(String(err)),
505
- });
506
-
507
996
  const grantsByResource = (grants || []).reduce(
508
997
  (acc: Record<string, any[]>, g: any) => {
509
998
  if (!acc[g.resourceId]) acc[g.resourceId] = [];
@@ -535,7 +1024,7 @@ export default function WorkspaceRoute() {
535
1024
  {Array.from({ length: 3 }).map((_, index) => (
536
1025
  <div
537
1026
  key={index}
538
- className="rounded-2xl border bg-card px-5 py-4 space-y-2"
1027
+ className="rounded-lg border bg-card px-5 py-4 space-y-2"
539
1028
  >
540
1029
  <Skeleton className="h-4 w-1/3" />
541
1030
  <Skeleton className="h-3 w-2/3" />
@@ -546,7 +1035,7 @@ export default function WorkspaceRoute() {
546
1035
  }
547
1036
  if (items.length === 0) {
548
1037
  return (
549
- <div className="rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground">
1038
+ <div className="rounded-lg border border-dashed px-6 py-12 text-center text-sm text-muted-foreground">
550
1039
  {emptyText}
551
1040
  </div>
552
1041
  );
@@ -567,7 +1056,7 @@ export default function WorkspaceRoute() {
567
1056
  return (
568
1057
  <DispatchShell
569
1058
  title="Workspace Resources"
570
- description="Share skills, instructions, agent profiles, and knowledge packs across workspace apps. Scope to all apps or grant per-app."
1059
+ description="Manage inherited workspace skills, guardrail instructions, agent profiles, and reference resources. All-app resources are available to every app without syncing."
571
1060
  >
572
1061
  <div className="flex items-center justify-between">
573
1062
  <div className="text-sm text-muted-foreground">
@@ -578,21 +1067,12 @@ export default function WorkspaceRoute() {
578
1067
  )}
579
1068
  </div>
580
1069
  <div className="flex gap-2">
581
- <Button
582
- variant="outline"
583
- onClick={() => syncAll.mutate({})}
584
- disabled={syncAll.isPending || (resources?.length || 0) === 0}
585
- >
586
- <IconRefresh
587
- size={16}
588
- className={syncAll.isPending ? "mr-1.5 animate-spin" : "mr-1.5"}
589
- />
590
- Sync all
591
- </Button>
592
1070
  <AddResourceDialog />
593
1071
  </div>
594
1072
  </div>
595
1073
 
1074
+ <GlobalContextSection resources={resources || []} />
1075
+
596
1076
  <Tabs defaultValue="skills">
597
1077
  <TabsList>
598
1078
  <TabsTrigger value="skills">