@agent-native/core 0.90.3 → 0.90.5

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 (256) hide show
  1. package/bin/agent-native.js +25 -3
  2. package/corpus/README.md +1 -1
  3. package/corpus/core/CHANGELOG.md +17 -0
  4. package/corpus/core/bin/agent-native.js +25 -3
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/agent/durable-background.ts +18 -18
  7. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +8 -2
  8. package/corpus/core/src/agent/engine/anthropic-engine.ts +11 -1
  9. package/corpus/core/src/agent/engine/index.ts +1 -0
  10. package/corpus/core/src/agent/engine/registry.ts +63 -2
  11. package/corpus/core/src/agent/production-agent.ts +22 -11
  12. package/corpus/core/src/agent/run-manager.ts +17 -6
  13. package/corpus/core/src/agent/run-store.ts +16 -11
  14. package/corpus/core/src/cli/create.ts +35 -0
  15. package/corpus/core/src/cli/design-connect.ts +263 -1
  16. package/corpus/core/src/cli/index.ts +84 -7
  17. package/corpus/core/src/cli/workspacify.ts +34 -0
  18. package/corpus/core/src/client/KeepTabOpenNotice.tsx +20 -15
  19. package/corpus/core/src/client/RunStuckBanner.tsx +14 -13
  20. package/corpus/core/src/client/agent-chat-adapter.ts +28 -17
  21. package/corpus/core/src/client/history/VersionHistoryPanel.tsx +77 -47
  22. package/corpus/core/src/client/history/index.ts +3 -0
  23. package/corpus/core/src/client/history/use-history.ts +28 -0
  24. package/corpus/core/src/client/index.ts +8 -0
  25. package/corpus/core/src/client/review/index.ts +5 -0
  26. package/corpus/core/src/client/review/use-review.ts +40 -2
  27. package/corpus/core/src/client/use-run-stuck-detection.ts +8 -5
  28. package/corpus/core/src/history/actions/create-resource-version.ts +11 -8
  29. package/corpus/core/src/history/index.ts +0 -1
  30. package/corpus/core/src/history/registry.ts +24 -12
  31. package/corpus/core/src/history/store.ts +84 -53
  32. package/corpus/core/src/review/actions/consume-review-feedback.ts +15 -5
  33. package/corpus/core/src/review/actions/delete-review-comment.ts +15 -3
  34. package/corpus/core/src/review/actions/resolve-review-thread.ts +12 -5
  35. package/corpus/core/src/review/index.ts +0 -5
  36. package/corpus/core/src/review/registry.ts +24 -12
  37. package/corpus/core/src/review/store.ts +10 -7
  38. package/corpus/core/src/server/agent-chat-plugin.ts +6 -0
  39. package/corpus/core/src/server/credential-provider.ts +13 -3
  40. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +11 -11
  41. package/corpus/templates/assets/actions/generate-image.ts +64 -28
  42. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +1 -1
  43. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +91 -74
  44. package/corpus/templates/assets/server/lib/generation.ts +29 -4
  45. package/corpus/templates/assets/server/lib/image-processing.ts +131 -0
  46. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +24 -2
  47. package/corpus/templates/calendar/AGENTS.md +3 -0
  48. package/corpus/templates/calendar/actions/create-event.ts +8 -34
  49. package/corpus/templates/calendar/actions/event-action-helpers.ts +52 -0
  50. package/corpus/templates/calendar/actions/manage-event-draft.ts +7 -34
  51. package/corpus/templates/calendar/actions/update-event.ts +10 -23
  52. package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +47 -0
  53. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +23 -0
  54. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +59 -2
  55. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +40 -1
  56. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +34 -1
  57. package/corpus/templates/calendar/app/hooks/use-events.ts +6 -0
  58. package/corpus/templates/calendar/app/i18n/zh-TW.ts +4 -0
  59. package/corpus/templates/calendar/app/i18n-data.ts +40 -0
  60. package/corpus/templates/calendar/changelog/2026-07-07-you-can-mark-event-guests-as-optional-while-inviting-them-or.md +6 -0
  61. package/corpus/templates/calendar/server/handlers/events.ts +1 -0
  62. package/corpus/templates/calendar/server/lib/google-calendar.ts +4 -0
  63. package/corpus/templates/calendar/server/plugins/agent-chat.ts +1 -1
  64. package/corpus/templates/calendar/shared/api.ts +2 -0
  65. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +29 -3
  66. package/corpus/templates/clips/actions/generate-workflow.ts +19 -8
  67. package/corpus/templates/clips/actions/get-clips-ai-prefs.ts +36 -0
  68. package/corpus/templates/clips/actions/lib/clips-ai-prefs.ts +28 -0
  69. package/corpus/templates/clips/actions/regenerate-chapters.ts +21 -7
  70. package/corpus/templates/clips/actions/regenerate-summary.ts +26 -8
  71. package/corpus/templates/clips/actions/regenerate-title.ts +44 -7
  72. package/corpus/templates/clips/actions/update-clips-ai-prefs.ts +50 -0
  73. package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +7 -2
  74. package/corpus/templates/clips/app/hooks/use-auto-title.ts +25 -10
  75. package/corpus/templates/clips/app/i18n/ar-SA.ts +28 -6
  76. package/corpus/templates/clips/app/i18n/de-DE.ts +28 -6
  77. package/corpus/templates/clips/app/i18n/en-US.ts +29 -6
  78. package/corpus/templates/clips/app/i18n/es-ES.ts +29 -6
  79. package/corpus/templates/clips/app/i18n/fr-FR.ts +30 -6
  80. package/corpus/templates/clips/app/i18n/hi-IN.ts +26 -6
  81. package/corpus/templates/clips/app/i18n/ja-JP.ts +27 -6
  82. package/corpus/templates/clips/app/i18n/ko-KR.ts +28 -6
  83. package/corpus/templates/clips/app/i18n/pt-BR.ts +28 -6
  84. package/corpus/templates/clips/app/i18n/zh-CN.ts +26 -6
  85. package/corpus/templates/clips/app/i18n/zh-TW.ts +25 -6
  86. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +73 -0
  87. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -0
  88. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -2
  89. package/corpus/templates/clips/changelog/2026-07-07-ai-tools-can-include-the-full-video-not-just-the-transcript-.md +6 -0
  90. package/corpus/templates/clips/changelog/2026-07-07-clips-desktop-now-surfaces-meetings-and-dictate-directly-in-.md +6 -0
  91. package/corpus/templates/clips/changelog/2026-07-07-meeting-notes-now-explain-exactly-how-to-start-granola-style.md +6 -0
  92. package/corpus/templates/clips/changelog/2026-07-07-meeting-start-alerts-now-include-separate-start-notes-and-jo.md +6 -0
  93. package/corpus/templates/clips/changelog/2026-07-07-recordings-start-more-predictably-and-newly-saved-clips-refr.md +6 -0
  94. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  95. package/corpus/templates/clips/desktop/src/app.tsx +435 -105
  96. package/corpus/templates/clips/desktop/src/lib/recorder.ts +27 -21
  97. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -22
  98. package/corpus/templates/clips/desktop/src/styles.css +167 -2
  99. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  100. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -7
  101. package/corpus/templates/clips/desktop/src-tauri/src/tray_meetings.rs +5 -4
  102. package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.put.ts +16 -8
  103. package/corpus/templates/clips/shared/clips-ai-prefs.ts +81 -0
  104. package/corpus/templates/content/AGENTS.md +1 -0
  105. package/corpus/templates/content/actions/_database-source-utils.ts +73 -8
  106. package/corpus/templates/content/actions/_database-utils.ts +2 -1
  107. package/corpus/templates/content/actions/prepare-builder-source-review.ts +20 -1
  108. package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +663 -0
  109. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +33 -7
  110. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +53 -4
  111. package/corpus/templates/content/app/components/editor/database/settings.tsx +24 -0
  112. package/corpus/templates/content/app/hooks/use-content-database.ts +70 -1
  113. package/corpus/templates/content/app/hooks/use-document-properties.ts +63 -5
  114. package/corpus/templates/content/app/i18n/zh-TW.ts +3 -0
  115. package/corpus/templates/content/app/i18n-data.ts +31 -0
  116. package/corpus/templates/content/changelog/2026-07-01-builder-source-rows-can-be-staged-in-bulk-with-a-reviewable.md +6 -0
  117. package/corpus/templates/content/changelog/2026-07-07-builder-backed-database-fields-now-edit-like-normal-content-fields.md +6 -0
  118. package/corpus/templates/content/parity/matrix.md +24 -24
  119. package/corpus/templates/content/parity/matrix.ts +2 -0
  120. package/corpus/templates/content/shared/api.ts +59 -4
  121. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +16 -1
  122. package/corpus/templates/design/actions/add-localhost-screens.ts +1 -0
  123. package/corpus/templates/design/actions/connect-localhost.ts +26 -3
  124. package/corpus/templates/design/actions/open-visual-edit.ts +4 -37
  125. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +362 -12
  126. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +89 -14
  127. package/corpus/templates/design/app/components/design/bridge-security.ts +7 -1
  128. package/corpus/templates/design/app/components/design/multi-screen/types.ts +1 -1
  129. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
  130. package/corpus/templates/design/app/i18n-data.ts +41 -0
  131. package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-keeps-bridge-access-when-opening-previousl.md +6 -0
  132. package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-now-keeps-the-live-app-connection-authenti.md +6 -0
  133. package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-uses-live-app-iframes-for-click-to-edit-pr.md +6 -0
  134. package/dist/agent/durable-background.d.ts +12 -12
  135. package/dist/agent/durable-background.js +18 -18
  136. package/dist/agent/durable-background.js.map +1 -1
  137. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  138. package/dist/agent/engine/ai-sdk-engine.js +8 -2
  139. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  140. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  141. package/dist/agent/engine/anthropic-engine.js +11 -1
  142. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  143. package/dist/agent/engine/index.d.ts +1 -1
  144. package/dist/agent/engine/index.d.ts.map +1 -1
  145. package/dist/agent/engine/index.js +1 -1
  146. package/dist/agent/engine/index.js.map +1 -1
  147. package/dist/agent/engine/registry.d.ts +14 -0
  148. package/dist/agent/engine/registry.d.ts.map +1 -1
  149. package/dist/agent/engine/registry.js +56 -2
  150. package/dist/agent/engine/registry.js.map +1 -1
  151. package/dist/agent/production-agent.d.ts +2 -2
  152. package/dist/agent/production-agent.d.ts.map +1 -1
  153. package/dist/agent/production-agent.js +18 -11
  154. package/dist/agent/production-agent.js.map +1 -1
  155. package/dist/agent/run-manager.d.ts +7 -1
  156. package/dist/agent/run-manager.d.ts.map +1 -1
  157. package/dist/agent/run-manager.js +6 -1
  158. package/dist/agent/run-manager.js.map +1 -1
  159. package/dist/agent/run-store.d.ts +1 -1
  160. package/dist/agent/run-store.d.ts.map +1 -1
  161. package/dist/agent/run-store.js +15 -10
  162. package/dist/agent/run-store.js.map +1 -1
  163. package/dist/cli/create.d.ts.map +1 -1
  164. package/dist/cli/create.js +31 -0
  165. package/dist/cli/create.js.map +1 -1
  166. package/dist/cli/design-connect.d.ts.map +1 -1
  167. package/dist/cli/design-connect.js +197 -1
  168. package/dist/cli/design-connect.js.map +1 -1
  169. package/dist/cli/index.js +76 -9
  170. package/dist/cli/index.js.map +1 -1
  171. package/dist/cli/workspacify.d.ts.map +1 -1
  172. package/dist/cli/workspacify.js +30 -0
  173. package/dist/cli/workspacify.js.map +1 -1
  174. package/dist/client/KeepTabOpenNotice.d.ts +9 -10
  175. package/dist/client/KeepTabOpenNotice.d.ts.map +1 -1
  176. package/dist/client/KeepTabOpenNotice.js +8 -4
  177. package/dist/client/KeepTabOpenNotice.js.map +1 -1
  178. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  179. package/dist/client/RunStuckBanner.js +14 -13
  180. package/dist/client/RunStuckBanner.js.map +1 -1
  181. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  182. package/dist/client/agent-chat-adapter.js +25 -17
  183. package/dist/client/agent-chat-adapter.js.map +1 -1
  184. package/dist/client/history/VersionHistoryPanel.d.ts +1 -1
  185. package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -1
  186. package/dist/client/history/VersionHistoryPanel.js +12 -5
  187. package/dist/client/history/VersionHistoryPanel.js.map +1 -1
  188. package/dist/client/history/index.d.ts +1 -1
  189. package/dist/client/history/index.d.ts.map +1 -1
  190. package/dist/client/history/index.js +1 -1
  191. package/dist/client/history/index.js.map +1 -1
  192. package/dist/client/history/use-history.d.ts +12 -0
  193. package/dist/client/history/use-history.d.ts.map +1 -1
  194. package/dist/client/history/use-history.js +6 -0
  195. package/dist/client/history/use-history.js.map +1 -1
  196. package/dist/client/index.d.ts +2 -2
  197. package/dist/client/index.d.ts.map +1 -1
  198. package/dist/client/index.js +2 -2
  199. package/dist/client/index.js.map +1 -1
  200. package/dist/client/review/index.d.ts +1 -1
  201. package/dist/client/review/index.d.ts.map +1 -1
  202. package/dist/client/review/index.js +1 -1
  203. package/dist/client/review/index.js.map +1 -1
  204. package/dist/client/review/use-review.d.ts +23 -0
  205. package/dist/client/review/use-review.d.ts.map +1 -1
  206. package/dist/client/review/use-review.js +8 -0
  207. package/dist/client/review/use-review.js.map +1 -1
  208. package/dist/client/use-run-stuck-detection.d.ts +1 -1
  209. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  210. package/dist/client/use-run-stuck-detection.js +6 -4
  211. package/dist/client/use-run-stuck-detection.js.map +1 -1
  212. package/dist/collab/struct-routes.d.ts +1 -1
  213. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  214. package/dist/history/actions/create-resource-version.js +7 -4
  215. package/dist/history/actions/create-resource-version.js.map +1 -1
  216. package/dist/history/index.d.ts +1 -1
  217. package/dist/history/index.d.ts.map +1 -1
  218. package/dist/history/index.js +1 -1
  219. package/dist/history/index.js.map +1 -1
  220. package/dist/history/registry.d.ts.map +1 -1
  221. package/dist/history/registry.js +14 -12
  222. package/dist/history/registry.js.map +1 -1
  223. package/dist/history/store.d.ts.map +1 -1
  224. package/dist/history/store.js +74 -49
  225. package/dist/history/store.js.map +1 -1
  226. package/dist/notifications/routes.d.ts +1 -1
  227. package/dist/resources/handlers.d.ts +3 -3
  228. package/dist/review/actions/consume-review-feedback.d.ts +1 -0
  229. package/dist/review/actions/consume-review-feedback.js +8 -2
  230. package/dist/review/actions/consume-review-feedback.js.map +1 -1
  231. package/dist/review/actions/delete-review-comment.d.ts +2 -1
  232. package/dist/review/actions/delete-review-comment.js +11 -3
  233. package/dist/review/actions/delete-review-comment.js.map +1 -1
  234. package/dist/review/actions/resolve-review-thread.d.ts +2 -1
  235. package/dist/review/actions/resolve-review-thread.js +5 -2
  236. package/dist/review/actions/resolve-review-thread.js.map +1 -1
  237. package/dist/review/index.d.ts +1 -1
  238. package/dist/review/index.d.ts.map +1 -1
  239. package/dist/review/index.js +1 -1
  240. package/dist/review/index.js.map +1 -1
  241. package/dist/review/registry.d.ts.map +1 -1
  242. package/dist/review/registry.js +14 -12
  243. package/dist/review/registry.js.map +1 -1
  244. package/dist/review/store.d.ts +3 -3
  245. package/dist/review/store.d.ts.map +1 -1
  246. package/dist/review/store.js +7 -4
  247. package/dist/review/store.js.map +1 -1
  248. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  249. package/dist/server/agent-chat-plugin.js +5 -0
  250. package/dist/server/agent-chat-plugin.js.map +1 -1
  251. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  252. package/dist/server/credential-provider.d.ts +2 -0
  253. package/dist/server/credential-provider.d.ts.map +1 -1
  254. package/dist/server/credential-provider.js +13 -3
  255. package/dist/server/credential-provider.js.map +1 -1
  256. package/package.json +1 -1
@@ -133,16 +133,15 @@ const MAX_HISTORY_LARGE_TOOL_ARGS_CHARS = 200_000;
133
133
  const STARTUP_RESPONSE_TIMEOUT_MS = 45_000;
134
134
 
135
135
  // ── Background follow mode ──────────────────────────────────────────────────
136
- // For background-dispatched runs (dispatchMode starts with "background") the
137
- // SERVER is the sole recovery brain: it chains continuation chunks itself
138
- // (fresh runId, same turnId), pre-inserts the successor run row BEFORE the old
139
- // chunk completes (so /runs/active shows an active run continuously across
140
- // chunk boundaries), and a server sweep reaps lost handoffs into loud terminal
141
- // errors. The client therefore never POSTs synthetic continuations for these
142
- // runs it demotes itself to a READER of server state: poll /runs/active,
143
- // (re)attach to whichever run of this turn is live, and fold its events into
144
- // the same assistant message. Read-only, so multiple tabs following the same
145
- // run are safe (no localStorage/Web-Locks claim needed).
136
+ // For server-continued runs (dispatchMode starts with "background" or equals
137
+ // "foreground-self-chain") the SERVER normally chains continuation chunks
138
+ // itself (fresh runId, same turnId), pre-inserts the successor run row BEFORE
139
+ // the old chunk completes (so /runs/active shows an active run continuously
140
+ // across chunk boundaries), and a server sweep reaps lost handoffs into loud
141
+ // terminal errors. The client demotes itself to a READER of server state for
142
+ // healthy handoffs. Foreground self-chain keeps one escape hatch: if the server
143
+ // reports that the self-dispatch itself failed before a successor claimed the
144
+ // run, the existing client auto-continue POST remains the fallback.
146
145
  const BACKGROUND_FOLLOW_POLL_INTERVAL_MS = 1_000;
147
146
  // How long the follow loop tolerates seeing NO active run for this turn before
148
147
  // treating the turn as ended. The server pre-inserts the successor row before
@@ -1687,12 +1686,24 @@ export function createAgentChatAdapter(
1687
1686
  if (mode) currentRunDispatchMode = mode;
1688
1687
  };
1689
1688
 
1690
- // Mode switch for recovery ownership: background-dispatched runs are
1691
- // recovered by the SERVER (chained continuations, sweeps); the client
1692
- // only follows. Foreground (null/"foreground"/unknown) keeps the full
1693
- // client-side continuation machinery unchanged.
1694
- const isBackgroundDispatch = () =>
1689
+ // Mode switch for recovery ownership: durable/background-dispatched runs
1690
+ // are always recovered by the server. Foreground self-chain follows the
1691
+ // server on healthy handoffs, but a loud self-dispatch failure falls back
1692
+ // to the client POST path because no successor run owns recovery yet.
1693
+ const isDurableBackgroundDispatch = () =>
1695
1694
  currentRunDispatchMode?.startsWith("background") === true;
1695
+ const isForegroundSelfChainDispatch = () =>
1696
+ currentRunDispatchMode === "foreground-self-chain";
1697
+ const shouldFollowServerContinuation = (
1698
+ signal?: AgentAutoContinueSignal,
1699
+ ) => {
1700
+ if (isDurableBackgroundDispatch()) return true;
1701
+ if (!isForegroundSelfChainDispatch()) return false;
1702
+ return (
1703
+ signal?.errorInfo?.errorCode !==
1704
+ "background_continuation_dispatch_failed"
1705
+ );
1706
+ };
1696
1707
 
1697
1708
  const rememberRunSeq = (seq: number) => {
1698
1709
  lastSeq = seq;
@@ -2966,7 +2977,7 @@ export function createAgentChatAdapter(
2966
2977
  // following of server state instead. This is the fix for the
2967
2978
  // client/server recovery race: client watchdog signals here are
2968
2979
  // just "reattach", not "recover".
2969
- if (isBackgroundDispatch() && threadId) {
2980
+ if (shouldFollowServerContinuation(err) && threadId) {
2970
2981
  yield* followBackgroundTurn(err);
2971
2982
  return;
2972
2983
  }
@@ -3145,7 +3156,7 @@ export function createAgentChatAdapter(
3145
3156
  // synthetic-continuation POST below. (An initial POST that failed
3146
3157
  // before any response headers arrived has no dispatch mode yet
3147
3158
  // and keeps the foreground startup-retry path.)
3148
- if (isBackgroundDispatch() && threadId) {
3159
+ if (shouldFollowServerContinuation() && threadId) {
3149
3160
  yield* followBackgroundTurn(
3150
3161
  new AgentAutoContinueSignal({ reason: "stream_ended" }),
3151
3162
  );
@@ -1,5 +1,5 @@
1
1
  import { IconClock, IconRestore, IconTimeline } from "@tabler/icons-react";
2
- import type { ReactNode } from "react";
2
+ import { useState, type ReactNode } from "react";
3
3
 
4
4
  import type { ResourceVersion } from "../../history/types.js";
5
5
  import { cn } from "../utils.js";
@@ -31,6 +31,7 @@ export function VersionHistoryPanel({
31
31
  }: VersionHistoryPanelProps) {
32
32
  const versions = useResourceVersions({ resourceType, resourceId, limit });
33
33
  const restore = useRestoreResourceVersion();
34
+ const [confirmRestoreId, setConfirmRestoreId] = useState<string | null>(null);
34
35
 
35
36
  return (
36
37
  <section
@@ -49,52 +50,81 @@ export function VersionHistoryPanel({
49
50
  Loading versions...
50
51
  </div>
51
52
  ) : versions.data?.versions.length ? (
52
- versions.data.versions.map((version) => (
53
- <article key={version.id} className="flex gap-3 px-4 py-3">
54
- <div className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted">
55
- <IconClock className="h-4 w-4 text-muted-foreground" />
56
- </div>
57
- <div className="min-w-0 flex-1">
58
- {renderVersion ? (
59
- renderVersion(version)
60
- ) : (
61
- <>
62
- <div className="flex flex-wrap items-center gap-x-2 gap-y-1">
63
- <h3 className="truncate text-sm font-medium">
64
- {version.title ?? `Version ${version.versionNumber}`}
65
- </h3>
66
- <span className="text-xs text-muted-foreground">
67
- {formatVersionDate(version.createdAt)}
68
- </span>
69
- </div>
70
- {version.summary ? (
71
- <p className="mt-1 text-sm text-muted-foreground">
72
- {version.summary}
73
- </p>
74
- ) : null}
75
- </>
76
- )}
77
- </div>
78
- <button
79
- type="button"
80
- className="inline-flex h-8 items-center gap-1.5 rounded-md border border-input px-2.5 text-xs font-medium hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50"
81
- disabled={restore.isPending}
82
- onClick={() =>
83
- restore.mutate(
84
- { id: version.id },
85
- {
86
- onSuccess: (result) => {
87
- onRestored?.(result.version, result.result);
88
- },
89
- },
90
- )
91
- }
92
- >
93
- <IconRestore className="h-3.5 w-3.5" />
94
- Restore
95
- </button>
96
- </article>
97
- ))
53
+ versions.data.versions.map((version) => {
54
+ const confirming = confirmRestoreId === version.id;
55
+ return (
56
+ <article key={version.id} className="flex gap-3 px-4 py-3">
57
+ <div className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted">
58
+ <IconClock className="h-4 w-4 text-muted-foreground" />
59
+ </div>
60
+ <div className="min-w-0 flex-1">
61
+ {renderVersion ? (
62
+ renderVersion(version)
63
+ ) : (
64
+ <>
65
+ <div className="flex flex-wrap items-center gap-x-2 gap-y-1">
66
+ <h3 className="truncate text-sm font-medium">
67
+ {version.title ?? `Version ${version.versionNumber}`}
68
+ </h3>
69
+ <span className="text-xs text-muted-foreground">
70
+ {formatVersionDate(version.createdAt)}
71
+ </span>
72
+ </div>
73
+ {version.summary ? (
74
+ <p className="mt-1 text-sm text-muted-foreground">
75
+ {version.summary}
76
+ </p>
77
+ ) : null}
78
+ </>
79
+ )}
80
+ </div>
81
+ <div className="flex shrink-0 items-center gap-2">
82
+ {confirming ? (
83
+ <>
84
+ <button
85
+ type="button"
86
+ className="inline-flex h-8 items-center rounded-md border border-input px-2.5 text-xs font-medium hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50"
87
+ disabled={restore.isPending}
88
+ onClick={() => setConfirmRestoreId(null)}
89
+ >
90
+ Cancel
91
+ </button>
92
+ <button
93
+ type="button"
94
+ className="inline-flex h-8 items-center gap-1.5 rounded-md border border-destructive bg-destructive px-2.5 text-xs font-medium text-destructive-foreground hover:bg-destructive/90 disabled:cursor-not-allowed disabled:opacity-50"
95
+ disabled={restore.isPending}
96
+ onClick={() =>
97
+ restore.mutate(
98
+ { id: version.id },
99
+ {
100
+ onSuccess: (result) => {
101
+ setConfirmRestoreId(null);
102
+ onRestored?.(result.version, result.result);
103
+ },
104
+ onError: () => setConfirmRestoreId(null),
105
+ },
106
+ )
107
+ }
108
+ >
109
+ <IconRestore className="h-3.5 w-3.5" />
110
+ Confirm restore
111
+ </button>
112
+ </>
113
+ ) : (
114
+ <button
115
+ type="button"
116
+ className="inline-flex h-8 items-center gap-1.5 rounded-md border border-input px-2.5 text-xs font-medium hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50"
117
+ disabled={restore.isPending}
118
+ onClick={() => setConfirmRestoreId(version.id)}
119
+ >
120
+ <IconRestore className="h-3.5 w-3.5" />
121
+ Restore
122
+ </button>
123
+ )}
124
+ </div>
125
+ </article>
126
+ );
127
+ })
98
128
  ) : (
99
129
  <div className="px-4 py-6 text-sm text-muted-foreground">
100
130
  {emptyState}
@@ -6,9 +6,12 @@ export {
6
6
  export {
7
7
  useCreateResourceVersion,
8
8
  useResourceHistory,
9
+ useResourceVersion,
9
10
  useResourceVersions,
10
11
  useRestoreResourceVersion,
11
12
  type CreateResourceVersionInput,
13
+ type GetResourceVersionInput,
14
+ type GetResourceVersionResult,
12
15
  type ListResourceHistoryResult,
13
16
  type ListResourceVersionsResult,
14
17
  type ResourceHistoryParams,
@@ -30,6 +30,13 @@ export interface RestoreResourceVersionInput {
30
30
  versionNumber?: number;
31
31
  }
32
32
 
33
+ export interface GetResourceVersionInput {
34
+ id?: string;
35
+ resourceType?: string;
36
+ resourceId?: string;
37
+ versionNumber?: number;
38
+ }
39
+
33
40
  export interface ListResourceVersionsResult {
34
41
  versions: ResourceVersion[];
35
42
  }
@@ -39,6 +46,10 @@ export interface ListResourceHistoryResult {
39
46
  auditEvents: unknown[];
40
47
  }
41
48
 
49
+ export interface GetResourceVersionResult {
50
+ version: ResourceVersion;
51
+ }
52
+
42
53
  export interface RestoreResourceVersionResult {
43
54
  version: ResourceVersion;
44
55
  result: unknown;
@@ -72,6 +83,23 @@ export function useResourceHistory(
72
83
  );
73
84
  }
74
85
 
86
+ export function useResourceVersion(
87
+ params: GetResourceVersionInput,
88
+ options?: { enabled?: boolean },
89
+ ) {
90
+ const enabled =
91
+ options?.enabled ??
92
+ Boolean(
93
+ params.id ||
94
+ (params.resourceType && params.resourceId && params.versionNumber),
95
+ );
96
+ return useActionQuery<GetResourceVersionResult>(
97
+ "get-resource-version",
98
+ params,
99
+ { enabled },
100
+ );
101
+ }
102
+
75
103
  export function useCreateResourceVersion() {
76
104
  return useActionMutation<ResourceVersion, CreateResourceVersionInput>(
77
105
  "create-resource-version",
@@ -791,9 +791,12 @@ export {
791
791
  VersionHistoryPanel,
792
792
  useCreateResourceVersion,
793
793
  useResourceHistory,
794
+ useResourceVersion,
794
795
  useResourceVersions,
795
796
  useRestoreResourceVersion,
796
797
  type CreateResourceVersionInput,
798
+ type GetResourceVersionInput,
799
+ type GetResourceVersionResult,
797
800
  type ListResourceHistoryResult,
798
801
  type ListResourceVersionsResult,
799
802
  type ResourceHistoryParams,
@@ -806,14 +809,19 @@ export {
806
809
  ReviewStatusBadge,
807
810
  ReviewThreadPanel,
808
811
  buildReviewThreads,
812
+ useConsumeReviewFeedback,
809
813
  useCreateReviewComment,
810
814
  useDeleteReviewComment,
811
815
  useReplyReviewComment,
812
816
  useResolveReviewThread,
813
817
  useReviewComments,
818
+ useReviewFeedback,
814
819
  useSetReviewStatus,
820
+ type ConsumeReviewFeedbackInput,
815
821
  type CreateReviewCommentInput,
816
822
  type DeleteReviewCommentInput,
823
+ type GetReviewFeedbackParams,
824
+ type GetReviewFeedbackResult,
817
825
  type ListReviewCommentsParams,
818
826
  type ListReviewCommentsResult,
819
827
  type ReplyReviewCommentInput,
@@ -9,14 +9,19 @@ export {
9
9
  type ReviewThreadPanelProps,
10
10
  } from "./ReviewThreadPanel.js";
11
11
  export {
12
+ useConsumeReviewFeedback,
12
13
  useCreateReviewComment,
13
14
  useDeleteReviewComment,
14
15
  useReplyReviewComment,
15
16
  useResolveReviewThread,
16
17
  useReviewComments,
18
+ useReviewFeedback,
17
19
  useSetReviewStatus,
20
+ type ConsumeReviewFeedbackInput,
18
21
  type CreateReviewCommentInput,
19
22
  type DeleteReviewCommentInput,
23
+ type GetReviewFeedbackParams,
24
+ type GetReviewFeedbackResult,
20
25
  type ListReviewCommentsParams,
21
26
  type ListReviewCommentsResult,
22
27
  type ReplyReviewCommentInput,
@@ -22,6 +22,17 @@ export interface ListReviewCommentsResult {
22
22
  reviewStatus: ReviewStatusEntry | null;
23
23
  }
24
24
 
25
+ export interface GetReviewFeedbackParams {
26
+ resourceType: string;
27
+ resourceId: string;
28
+ includeHumanTargeted?: boolean;
29
+ limit?: number;
30
+ }
31
+
32
+ export interface GetReviewFeedbackResult {
33
+ comments: ReviewComment[];
34
+ }
35
+
25
36
  export interface CreateReviewCommentInput {
26
37
  resourceType: string;
27
38
  resourceId: string;
@@ -59,6 +70,12 @@ export interface DeleteReviewCommentInput {
59
70
  commentId: string;
60
71
  }
61
72
 
73
+ export interface ConsumeReviewFeedbackInput {
74
+ resourceType: string;
75
+ resourceId: string;
76
+ commentIds: string[];
77
+ }
78
+
62
79
  export interface SetReviewStatusInput {
63
80
  resourceType: string;
64
81
  resourceId: string;
@@ -81,6 +98,20 @@ export function useReviewComments(
81
98
  );
82
99
  }
83
100
 
101
+ export function useReviewFeedback(
102
+ params: GetReviewFeedbackParams,
103
+ options?: { enabled?: boolean },
104
+ ) {
105
+ return useActionQuery<GetReviewFeedbackResult>(
106
+ "get-review-feedback",
107
+ params,
108
+ {
109
+ enabled:
110
+ options?.enabled ?? Boolean(params.resourceType && params.resourceId),
111
+ },
112
+ );
113
+ }
114
+
84
115
  export function useCreateReviewComment() {
85
116
  return useActionMutation<ReviewComment, CreateReviewCommentInput>(
86
117
  "create-review-comment",
@@ -95,18 +126,25 @@ export function useReplyReviewComment() {
95
126
 
96
127
  export function useResolveReviewThread() {
97
128
  return useActionMutation<
98
- { threadId: string; resolved: true },
129
+ { threadId: string; resolved: true; updatedCount: number },
99
130
  ResolveReviewThreadInput
100
131
  >("resolve-review-thread");
101
132
  }
102
133
 
103
134
  export function useDeleteReviewComment() {
104
135
  return useActionMutation<
105
- { commentId: string; deleted: true },
136
+ { commentId: string; deleted: true; updatedCount: number },
106
137
  DeleteReviewCommentInput
107
138
  >("delete-review-comment");
108
139
  }
109
140
 
141
+ export function useConsumeReviewFeedback() {
142
+ return useActionMutation<
143
+ { consumedCommentIds: string[]; updatedCount: number },
144
+ ConsumeReviewFeedbackInput
145
+ >("consume-review-feedback");
146
+ }
147
+
110
148
  export function useSetReviewStatus() {
111
149
  return useActionMutation<ReviewStatusEntry, SetReviewStatusInput>(
112
150
  "set-review-status",
@@ -26,7 +26,7 @@ export interface RunStuckState {
26
26
  heartbeatAt: number | null;
27
27
  /** Milliseconds since `heartbeatAt`, computed against the server clock. */
28
28
  heartbeatSinceMs: number | null;
29
- /** How the run was dispatched, e.g. foreground or background-processing. */
29
+ /** How the run was dispatched/continued, e.g. foreground-self-chain or background-processing. */
30
30
  dispatchMode: string | null;
31
31
  }
32
32
 
@@ -133,10 +133,13 @@ export function useRunStuckDetection({
133
133
  heartbeatAt != null ? nowMs - heartbeatAt : null;
134
134
  const dispatchMode =
135
135
  typeof data.dispatchMode === "string" ? data.dispatchMode : null;
136
- // Background-dispatched runs get the wider threshold: the server's
137
- // own recovery (150s no-progress backstop + chained continuations)
138
- // must get its chance before the user sees a "stuck" affordance.
139
- const effectiveThresholdMs = dispatchMode?.startsWith("background")
136
+ // Server-continued runs get the wider threshold: the server's own
137
+ // recovery (150s no-progress backstop + chained continuations) must
138
+ // get its chance before the user sees a "stuck" affordance.
139
+ const serverContinued =
140
+ dispatchMode === "foreground-self-chain" ||
141
+ dispatchMode?.startsWith("background") === true;
142
+ const effectiveThresholdMs = serverContinued
140
143
  ? backgroundStuckThresholdMs
141
144
  : stuckThresholdMs;
142
145
  const isStuck = Boolean(
@@ -30,15 +30,18 @@ export default defineAction({
30
30
  "editor",
31
31
  );
32
32
  const registration = getVersionedResource(args.resourceType);
33
+ // Prefer the registered server snapshot when available so editors/agents
34
+ // cannot plant arbitrary restore payloads for managed resource types.
35
+ const serverSnapshot = registration?.getSnapshot
36
+ ? await registration.getSnapshot({
37
+ resourceType: args.resourceType,
38
+ resourceId: args.resourceId,
39
+ ctx: actionCtx,
40
+ access,
41
+ })
42
+ : undefined;
33
43
  const snapshot =
34
- args.snapshot !== undefined
35
- ? args.snapshot
36
- : await registration?.getSnapshot?.({
37
- resourceType: args.resourceType,
38
- resourceId: args.resourceId,
39
- ctx: actionCtx,
40
- access,
41
- });
44
+ serverSnapshot !== undefined ? serverSnapshot : args.snapshot;
42
45
 
43
46
  if (snapshot === undefined) {
44
47
  throw new Error(
@@ -22,6 +22,5 @@ export {
22
22
  ensureResourceVersionsTable,
23
23
  getResourceVersionById,
24
24
  getResourceVersionByNumber,
25
- insertResourceVersion,
26
25
  queryResourceVersions,
27
26
  } from "./store.js";
@@ -1,4 +1,8 @@
1
- import { ForbiddenError, resolveAccess } from "../sharing/access.js";
1
+ import {
2
+ ForbiddenError,
3
+ resolveAccess,
4
+ type AccessContext,
5
+ } from "../sharing/access.js";
2
6
  import { getShareableResource } from "../sharing/registry.js";
3
7
  import { roleSatisfies, type Visibility } from "../sharing/schema.js";
4
8
  import type {
@@ -31,6 +35,16 @@ export function listVersionedResources(): VersionedResourceRegistration[] {
31
35
  return Array.from(registrations.values());
32
36
  }
33
37
 
38
+ function accessContextFrom(
39
+ ctx?: VersionedResourceContext,
40
+ ): AccessContext | undefined {
41
+ if (!ctx) return undefined;
42
+ return {
43
+ userEmail: ctx.userEmail ?? undefined,
44
+ orgId: ctx.orgId ?? undefined,
45
+ };
46
+ }
47
+
34
48
  export async function resolveVersionedResourceAccess(
35
49
  resourceType: string,
36
50
  resourceId: string,
@@ -42,7 +56,11 @@ export async function resolveVersionedResourceAccess(
42
56
  }
43
57
 
44
58
  if (getShareableResource(resourceType)) {
45
- const access = await resolveAccess(resourceType, resourceId);
59
+ const access = await resolveAccess(
60
+ resourceType,
61
+ resourceId,
62
+ accessContextFrom(ctx),
63
+ );
46
64
  if (!access) {
47
65
  return null;
48
66
  }
@@ -55,16 +73,10 @@ export async function resolveVersionedResourceAccess(
55
73
  };
56
74
  }
57
75
 
58
- const userEmail = ctx?.userEmail ?? null;
59
- if (!userEmail) {
60
- return null;
61
- }
62
- return {
63
- role: "owner",
64
- ownerEmail: userEmail,
65
- orgId: ctx?.orgId ?? null,
66
- visibility: "private",
67
- };
76
+ // Fail closed: unregistered types, and registered types without an access
77
+ // resolver that aren't shareable, never invent ownership. bypassScope on
78
+ // history queries is only safe after a real resource ACL.
79
+ return null;
68
80
  }
69
81
 
70
82
  export async function assertVersionedResourceAccess(