@agent-native/core 0.101.7 → 0.101.11

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 (219) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +28 -0
  3. package/corpus/core/docs/content/deployment.mdx +9 -7
  4. package/corpus/core/docs/content/locales/ar-SA/deployment.mdx +9 -7
  5. package/corpus/core/docs/content/locales/de-DE/deployment.mdx +9 -7
  6. package/corpus/core/docs/content/locales/es-ES/deployment.mdx +9 -7
  7. package/corpus/core/docs/content/locales/fr-FR/deployment.mdx +9 -7
  8. package/corpus/core/docs/content/locales/hi-IN/deployment.mdx +9 -7
  9. package/corpus/core/docs/content/locales/ja-JP/deployment.mdx +9 -7
  10. package/corpus/core/docs/content/locales/ko-KR/deployment.mdx +9 -7
  11. package/corpus/core/docs/content/locales/pt-BR/deployment.mdx +9 -7
  12. package/corpus/core/docs/content/locales/zh-CN/deployment.mdx +9 -7
  13. package/corpus/core/docs/content/locales/zh-TW/deployment.mdx +9 -7
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/a2a/client.ts +23 -1
  16. package/corpus/core/src/a2a/handlers.ts +65 -1
  17. package/corpus/core/src/a2a/server.ts +124 -0
  18. package/corpus/core/src/a2a/task-store.ts +253 -0
  19. package/corpus/core/src/a2a/types.ts +26 -0
  20. package/corpus/core/src/agent/production-agent.ts +5 -2
  21. package/corpus/core/src/client/review/ReviewCommentComposer.tsx +9 -1
  22. package/corpus/core/src/client/review/ReviewThreadPanel.tsx +17 -2
  23. package/corpus/core/src/client/settings/SettingsPanel.tsx +41 -21
  24. package/corpus/core/src/client/settings/useBuilderStatus.ts +28 -9
  25. package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +2 -7
  26. package/corpus/core/src/mcp/builtin-tools.ts +25 -1
  27. package/corpus/core/src/scripts/call-agent.ts +22 -3
  28. package/corpus/core/src/server/agent-chat-plugin.ts +86 -1
  29. package/corpus/core/src/server/auth.ts +25 -1
  30. package/corpus/core/src/server/builder-browser.ts +353 -1
  31. package/corpus/core/src/server/core-routes-plugin.ts +601 -223
  32. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
  33. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +80 -1
  34. package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +28 -3
  35. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +5 -1
  36. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +30 -3
  37. package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +196 -0
  38. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +115 -1
  39. package/corpus/templates/calendar/app/lib/out-of-office.ts +45 -0
  40. package/corpus/templates/calendar/changelog/2026-07-14-out-of-office-blocks-now-sit-behind-meetings-and-event-stack.md +6 -0
  41. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +23 -0
  42. package/corpus/templates/content/AGENTS.md +3 -2
  43. package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +87 -0
  44. package/corpus/templates/content/actions/_builder-cms-read-client.ts +173 -25
  45. package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
  46. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +427 -18
  47. package/corpus/templates/content/actions/_builder-cms-write-client.ts +17 -73
  48. package/corpus/templates/content/actions/_builder-cms-write-settings.ts +27 -7
  49. package/corpus/templates/content/actions/_builder-source-execution-claim.ts +124 -0
  50. package/corpus/templates/content/actions/_builder-source-execution-preservation.ts +21 -0
  51. package/corpus/templates/content/actions/_builder-source-timings.ts +57 -0
  52. package/corpus/templates/content/actions/_database-source-utils.ts +1041 -154
  53. package/corpus/templates/content/actions/_database-utils.ts +89 -12
  54. package/corpus/templates/content/actions/_preview-document-draft.ts +31 -0
  55. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +309 -0
  56. package/corpus/templates/content/actions/execute-builder-source-batch.ts +120 -49
  57. package/corpus/templates/content/actions/execute-builder-source-execution.ts +608 -264
  58. package/corpus/templates/content/actions/get-preview-document-draft.ts +26 -0
  59. package/corpus/templates/content/actions/materialize-builder-required-fields.ts +550 -0
  60. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +112 -65
  61. package/corpus/templates/content/actions/prepare-builder-source-review.ts +463 -170
  62. package/corpus/templates/content/actions/preview-builder-source-review.ts +99 -0
  63. package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +2 -2
  64. package/corpus/templates/content/actions/update-preview-document-draft.ts +153 -0
  65. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +27 -15
  66. package/corpus/templates/content/app/components/editor/MathRenderer.tsx +43 -0
  67. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +260 -4
  68. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +57 -5
  69. package/corpus/templates/content/app/components/editor/body-hydration.ts +27 -0
  70. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2353 -576
  71. package/corpus/templates/content/app/components/editor/database/settings.tsx +13 -10
  72. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +358 -75
  73. package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +78 -0
  74. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +124 -29
  75. package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +2 -0
  76. package/corpus/templates/content/app/global.css +71 -1
  77. package/corpus/templates/content/app/hooks/use-content-database.ts +68 -0
  78. package/corpus/templates/content/app/hooks/use-documents.ts +53 -4
  79. package/corpus/templates/content/app/i18n/zh-TW.ts +38 -0
  80. package/corpus/templates/content/app/i18n-data.ts +450 -0
  81. package/corpus/templates/content/app/root.tsx +2 -0
  82. package/corpus/templates/content/changelog/2026-07-10-builder-backed-preview-edits-are-preserved-when-article-bodi.md +6 -0
  83. package/corpus/templates/content/changelog/2026-07-10-builder-cms-sources-can-now-be-enabled-for-guarded-staged-or.md +6 -0
  84. package/corpus/templates/content/changelog/2026-07-13-builder-connection-setup-is-now-available-directly-from-conn.md +6 -0
  85. package/corpus/templates/content/changelog/2026-07-13-builder-reviews-now-hide-already-applied-writes-detect-nativ.md +6 -0
  86. package/corpus/templates/content/changelog/2026-07-13-required-builder-publishing-fields-can-now-be-added-directly.md +6 -0
  87. package/corpus/templates/content/changelog/2026-07-13-you-can-now-cancel-a-prepared-builder-update-safely-before-i.md +6 -0
  88. package/corpus/templates/content/changelog/2026-07-14-builder-account-connection-now-remains-available-when-branch.md +6 -0
  89. package/corpus/templates/content/changelog/2026-07-14-builder-backed-articles-now-preserve-fifth-and-sixth-level-h.md +6 -0
  90. package/corpus/templates/content/changelog/2026-07-14-builder-sources-now-review-and-publish-rich-article-updates-.md +6 -0
  91. package/corpus/templates/content/changelog/2026-07-14-builder-writes-now-stop-retries-when-reconciliation-is-needed.md +6 -0
  92. package/corpus/templates/content/changelog/2026-07-14-file-and-media-links-now-validate-before-saving.md +6 -0
  93. package/corpus/templates/content/changelog/2026-07-14-latex-equations-render-in-documents-public-pages-and-exports.md +6 -0
  94. package/corpus/templates/content/package.json +1 -0
  95. package/corpus/templates/content/parity/matrix.md +27 -25
  96. package/corpus/templates/content/parity/matrix.ts +50 -2
  97. package/corpus/templates/content/server/db/schema.ts +47 -0
  98. package/corpus/templates/content/server/plugins/auth.ts +1 -0
  99. package/corpus/templates/content/server/plugins/db.ts +53 -0
  100. package/corpus/templates/content/shared/api.ts +70 -1
  101. package/corpus/templates/content/shared/builder-mdx.ts +705 -15
  102. package/corpus/templates/content/shared/document-export.ts +296 -25
  103. package/corpus/templates/content/shared/inline-math.ts +128 -0
  104. package/corpus/templates/content/shared/math-rendering.ts +50 -0
  105. package/corpus/templates/content/shared/nfm.ts +20 -17
  106. package/corpus/templates/content/vite.config.ts +0 -1
  107. package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +52 -7
  108. package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +36 -4
  109. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +4 -1
  110. package/corpus/templates/design/app/i18n/ar-SA.ts +1 -0
  111. package/corpus/templates/design/app/i18n/de-DE.ts +1 -0
  112. package/corpus/templates/design/app/i18n/en-US.ts +1 -0
  113. package/corpus/templates/design/app/i18n/es-ES.ts +1 -0
  114. package/corpus/templates/design/app/i18n/fr-FR.ts +1 -0
  115. package/corpus/templates/design/app/i18n/hi-IN.ts +1 -0
  116. package/corpus/templates/design/app/i18n/ja-JP.ts +1 -0
  117. package/corpus/templates/design/app/i18n/ko-KR.ts +1 -0
  118. package/corpus/templates/design/app/i18n/pt-BR.ts +1 -0
  119. package/corpus/templates/design/app/i18n/zh-CN.ts +1 -0
  120. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  121. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +8 -0
  122. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +11 -0
  123. package/corpus/templates/design/changelog/2026-07-14-reviewers-can-now-pin-comments-from-the-canvas-toolbar-targe.md +6 -0
  124. package/corpus/templates/design/changelog/2026-07-15-viewer-comment-controls-now-sit-inside-the-read-only-notice-.md +6 -0
  125. package/corpus/templates/design/shared/review-anchor.ts +48 -0
  126. package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +17 -0
  127. package/corpus/templates/mail/AGENTS.md +21 -19
  128. package/corpus/templates/mail/actions/create-attachment-upload.ts +49 -0
  129. package/corpus/templates/mail/changelog/2026-07-14-connected-agents-can-now-securely-upload-local-files-for-mai.md +6 -0
  130. package/corpus/templates/mail/server/handlers/media.ts +84 -61
  131. package/corpus/templates/mail/server/lib/attachment-upload-ticket.ts +234 -0
  132. package/corpus/templates/mail/server/lib/mail-connector-catalog.ts +8 -4
  133. package/corpus/templates/mail/server/lib/media-upload.ts +114 -0
  134. package/corpus/templates/mail/server/plugins/auth.ts +9 -1
  135. package/corpus/templates/mail/server/routes/api/media/attachment-upload/[uploadId].put.ts +1 -0
  136. package/corpus/templates/plan/server/plan-mdx.ts +53 -7
  137. package/dist/a2a/client.d.ts +5 -1
  138. package/dist/a2a/client.d.ts.map +1 -1
  139. package/dist/a2a/client.js +18 -1
  140. package/dist/a2a/client.js.map +1 -1
  141. package/dist/a2a/handlers.d.ts.map +1 -1
  142. package/dist/a2a/handlers.js +57 -8
  143. package/dist/a2a/handlers.js.map +1 -1
  144. package/dist/a2a/server.d.ts.map +1 -1
  145. package/dist/a2a/server.js +87 -1
  146. package/dist/a2a/server.js.map +1 -1
  147. package/dist/a2a/task-store.d.ts +27 -0
  148. package/dist/a2a/task-store.d.ts.map +1 -1
  149. package/dist/a2a/task-store.js +216 -0
  150. package/dist/a2a/task-store.js.map +1 -1
  151. package/dist/a2a/types.d.ts +24 -0
  152. package/dist/a2a/types.d.ts.map +1 -1
  153. package/dist/a2a/types.js.map +1 -1
  154. package/dist/agent/production-agent.d.ts +1 -0
  155. package/dist/agent/production-agent.d.ts.map +1 -1
  156. package/dist/agent/production-agent.js +5 -2
  157. package/dist/agent/production-agent.js.map +1 -1
  158. package/dist/client/review/ReviewCommentComposer.d.ts +2 -1
  159. package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -1
  160. package/dist/client/review/ReviewCommentComposer.js +3 -3
  161. package/dist/client/review/ReviewCommentComposer.js.map +1 -1
  162. package/dist/client/review/ReviewThreadPanel.d.ts +9 -1
  163. package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
  164. package/dist/client/review/ReviewThreadPanel.js +6 -4
  165. package/dist/client/review/ReviewThreadPanel.js.map +1 -1
  166. package/dist/client/settings/SettingsPanel.d.ts +3 -0
  167. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  168. package/dist/client/settings/SettingsPanel.js +11 -4
  169. package/dist/client/settings/SettingsPanel.js.map +1 -1
  170. package/dist/client/settings/useBuilderStatus.d.ts +8 -4
  171. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  172. package/dist/client/settings/useBuilderStatus.js +19 -6
  173. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  174. package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -1
  175. package/dist/client/setup-connections/BuilderConnectCard.js +2 -5
  176. package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -1
  177. package/dist/collab/awareness.d.ts +2 -2
  178. package/dist/collab/awareness.d.ts.map +1 -1
  179. package/dist/collab/struct-routes.d.ts +1 -1
  180. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  181. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  182. package/dist/mcp/builtin-tools.js +22 -3
  183. package/dist/mcp/builtin-tools.js.map +1 -1
  184. package/dist/notifications/routes.d.ts +3 -3
  185. package/dist/resources/handlers.d.ts +2 -2
  186. package/dist/scripts/call-agent.d.ts +1 -1
  187. package/dist/scripts/call-agent.d.ts.map +1 -1
  188. package/dist/scripts/call-agent.js +19 -1
  189. package/dist/scripts/call-agent.js.map +1 -1
  190. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  191. package/dist/server/agent-chat-plugin.js +71 -2
  192. package/dist/server/agent-chat-plugin.js.map +1 -1
  193. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  194. package/dist/server/auth.d.ts +1 -0
  195. package/dist/server/auth.d.ts.map +1 -1
  196. package/dist/server/auth.js +19 -2
  197. package/dist/server/auth.js.map +1 -1
  198. package/dist/server/builder-browser.d.ts +78 -0
  199. package/dist/server/builder-browser.d.ts.map +1 -1
  200. package/dist/server/builder-browser.js +246 -1
  201. package/dist/server/builder-browser.js.map +1 -1
  202. package/dist/server/core-routes-plugin.d.ts +41 -0
  203. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  204. package/dist/server/core-routes-plugin.js +260 -14
  205. package/dist/server/core-routes-plugin.js.map +1 -1
  206. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
  207. package/docs/content/deployment.mdx +9 -7
  208. package/docs/content/locales/ar-SA/deployment.mdx +9 -7
  209. package/docs/content/locales/de-DE/deployment.mdx +9 -7
  210. package/docs/content/locales/es-ES/deployment.mdx +9 -7
  211. package/docs/content/locales/fr-FR/deployment.mdx +9 -7
  212. package/docs/content/locales/hi-IN/deployment.mdx +9 -7
  213. package/docs/content/locales/ja-JP/deployment.mdx +9 -7
  214. package/docs/content/locales/ko-KR/deployment.mdx +9 -7
  215. package/docs/content/locales/pt-BR/deployment.mdx +9 -7
  216. package/docs/content/locales/zh-CN/deployment.mdx +9 -7
  217. package/docs/content/locales/zh-TW/deployment.mdx +9 -7
  218. package/package.json +2 -2
  219. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +25 -0
@@ -1,21 +1,66 @@
1
- import { IconInfoCircle } from "@tabler/icons-react";
1
+ import { useT } from "@agent-native/core/client";
2
+ import { IconInfoCircle, IconMessage } from "@tabler/icons-react";
3
+
4
+ import { Button } from "@/components/ui/button";
5
+ import {
6
+ Tooltip,
7
+ TooltipContent,
8
+ TooltipTrigger,
9
+ } from "@/components/ui/tooltip";
10
+ import { cn } from "@/lib/utils";
2
11
 
3
12
  /**
4
13
  * Slim, non-blocking banner shown below the canvas when the current user only
5
14
  * has viewer access to the design (Figma-style "you can't edit this" notice).
6
- * Purely informational — editing affordances are already disabled elsewhere
7
- * via `canEditDesign`; this just tells the viewer why.
15
+ * Editing affordances are disabled elsewhere via `canEditDesign`; signed-in
16
+ * viewers get their one canvas action integrated into the same quiet notice.
8
17
  */
9
- export function ReadOnlyDesignBanner() {
18
+ export function ReadOnlyDesignBanner({
19
+ pinMode = false,
20
+ onCommentPin,
21
+ }: {
22
+ pinMode?: boolean;
23
+ onCommentPin?: () => void;
24
+ }) {
25
+ const t = useT();
26
+ const commentLabel = pinMode
27
+ ? t("designEditor.stopPinningComments")
28
+ : t("designEditor.pinComment");
29
+
10
30
  return (
11
- <div className="pointer-events-none absolute inset-x-0 bottom-0 z-30 flex items-center justify-center px-3 pb-2">
12
- <div className="flex items-center gap-2 rounded-full border border-blue-500/30 bg-blue-500/10 px-3 py-1.5 text-xs text-blue-600 shadow-sm backdrop-blur-sm dark:text-blue-400">
31
+ <div
32
+ data-read-only-design-banner
33
+ className="pointer-events-none absolute inset-x-0 bottom-0 z-[50] flex items-center justify-center px-3 pb-2"
34
+ >
35
+ <div className="flex max-w-full items-center gap-2 rounded-full border border-blue-500/30 bg-blue-500/10 py-1 pe-1 ps-3 text-xs text-blue-600 shadow-sm backdrop-blur-sm dark:text-blue-400">
13
36
  <IconInfoCircle className="size-3.5 shrink-0" />
14
- <span className="truncate">
37
+ <span className="min-w-0 truncate">
15
38
  {
16
39
  "You don't have access to edit this design" /* i18n-ignore Figma-style read-only notice */
17
40
  }
18
41
  </span>
42
+ {onCommentPin ? (
43
+ <Tooltip>
44
+ <TooltipTrigger asChild>
45
+ <Button
46
+ type="button"
47
+ variant="ghost"
48
+ size="icon"
49
+ className={cn(
50
+ "pointer-events-auto size-7 shrink-0 rounded-full text-blue-600 hover:bg-blue-500/15 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
51
+ pinMode &&
52
+ "bg-blue-500/20 text-blue-700 dark:bg-blue-400/20 dark:text-blue-300",
53
+ )}
54
+ aria-label={commentLabel}
55
+ aria-pressed={pinMode}
56
+ onClick={onCommentPin}
57
+ >
58
+ <IconMessage className="size-4" />
59
+ </Button>
60
+ </TooltipTrigger>
61
+ <TooltipContent side="top">{commentLabel}</TooltipContent>
62
+ </Tooltip>
63
+ ) : null}
19
64
  </div>
20
65
  </div>
21
66
  );
@@ -15,6 +15,9 @@ import { cn } from "@/lib/utils";
15
15
  export interface ReviewCommentsPanelProps {
16
16
  designId: string;
17
17
  activeFileId?: string | null;
18
+ commentAnchor?: unknown | null;
19
+ commentMetadata?: Record<string, unknown>;
20
+ commentContextLabel?: string;
18
21
  canComment: boolean;
19
22
  /** Caller-derived editor capability for resolving threads. */
20
23
  canResolve?: boolean;
@@ -30,9 +33,27 @@ export interface ReviewCommentsPanelProps {
30
33
  className?: string;
31
34
  }
32
35
 
36
+ type ReviewCommentsScope = "screen" | "all";
37
+
38
+ export function resolveReviewComposerTargetId({
39
+ scope,
40
+ activeFileId,
41
+ commentAnchor,
42
+ }: {
43
+ scope: ReviewCommentsScope;
44
+ activeFileId?: string | null;
45
+ commentAnchor?: unknown | null;
46
+ }): string | null | undefined {
47
+ if (commentAnchor != null) return activeFileId;
48
+ return scope === "screen" ? activeFileId : undefined;
49
+ }
50
+
33
51
  export function ReviewCommentsPanel({
34
52
  designId,
35
53
  activeFileId,
54
+ commentAnchor,
55
+ commentMetadata,
56
+ commentContextLabel,
36
57
  canComment,
37
58
  canResolve,
38
59
  canDeleteComment,
@@ -46,8 +67,13 @@ export function ReviewCommentsPanel({
46
67
  className,
47
68
  }: ReviewCommentsPanelProps) {
48
69
  const t = useT();
49
- const [scope, setScope] = useState<"screen" | "all">("screen");
70
+ const [scope, setScope] = useState<ReviewCommentsScope>("screen");
50
71
  const targetId = scope === "screen" ? activeFileId : undefined;
72
+ const composerTargetId = resolveReviewComposerTargetId({
73
+ scope,
74
+ activeFileId,
75
+ commentAnchor,
76
+ });
51
77
 
52
78
  return (
53
79
  <div
@@ -87,6 +113,10 @@ export function ReviewCommentsPanel({
87
113
  resourceType="design"
88
114
  resourceId={designId}
89
115
  targetId={targetId}
116
+ composerTargetId={composerTargetId}
117
+ composerAnchor={commentAnchor}
118
+ composerMetadata={commentMetadata}
119
+ composerContextLabel={commentContextLabel}
90
120
  title={t("review.panelTitle")}
91
121
  placeholder={t("review.placeholder")}
92
122
  emptyState={t("review.emptyState")}
@@ -104,13 +134,15 @@ export function ReviewCommentsPanel({
104
134
  variant="plain"
105
135
  showComposer={canComment && showComposer}
106
136
  canReply={canComment}
107
- canResolve={canResolve ?? canDispatchToAgent}
137
+ canResolve={canResolve ?? false}
108
138
  canDeleteComment={canDeleteComment}
109
- showComposerTargetPicker={canComment && showComposer}
139
+ showComposerTargetPicker={
140
+ canComment && showComposer && canDispatchToAgent
141
+ }
110
142
  composerCommentLabel={t("review.commentMode")}
111
143
  composerAgentLabel={t("review.sendToAgent")}
112
144
  onCommentCreated={(comment) => {
113
- if (comment.resolutionTarget === "agent") {
145
+ if (canDispatchToAgent && comment.resolutionTarget === "agent") {
114
146
  onDispatchCommentToAgent?.(comment);
115
147
  }
116
148
  }}
@@ -777,6 +777,7 @@ export function ReviewCanvasPins({
777
777
  postDraft({ ...draftPin, resolutionTarget });
778
778
  }}
779
779
  resolutionTarget={draftPin.resolutionTarget}
780
+ showAgentAction={Boolean(onDispatchCommentToAgent)}
780
781
  placement={getReviewPopoverPlacement(draftPinPosition.point)}
781
782
  submitting={createComment.isPending}
782
783
  />
@@ -843,6 +844,7 @@ function DraftComposer({
843
844
  onCancel,
844
845
  onSubmit,
845
846
  resolutionTarget,
847
+ showAgentAction,
846
848
  placement,
847
849
  submitting,
848
850
  }: {
@@ -851,6 +853,7 @@ function DraftComposer({
851
853
  onCancel: () => void;
852
854
  onSubmit: (target: "agent" | "human") => void;
853
855
  resolutionTarget: "agent" | "human";
856
+ showAgentAction: boolean;
854
857
  placement: ReviewPopoverPlacement;
855
858
  submitting: boolean;
856
859
  }) {
@@ -886,7 +889,7 @@ function DraftComposer({
886
889
  onChange={onChange}
887
890
  onSubmit={onSubmit}
888
891
  submittingTarget={submitting ? resolutionTarget : null}
889
- showAgentAction
892
+ showAgentAction={showAgentAction}
890
893
  placeholder={t("review.placeholder")}
891
894
  commentLabel={t("review.commentMode")}
892
895
  agentLabel={t("review.sendToAgent")}
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "سجّل الدخول للتعليق",
13
13
  panelTitle: "الملاحظات",
14
14
  placeholder: "اترك ملاحظاتك…",
15
+ commentingOn: "التعليق على {{name}}",
15
16
  emptyState: "لا توجد تعليقات مراجعة بعد.",
16
17
  clickToPin: "انقر في أي مكان لتثبيت ملاحظة",
17
18
  escToExit: "اضغط Esc للخروج",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "Zum Kommentieren anmelden",
13
13
  panelTitle: "Feedback",
14
14
  placeholder: "Feedback hinterlassen…",
15
+ commentingOn: "Kommentar zu {{name}}",
15
16
  emptyState: "Noch keine Prüfkommentare.",
16
17
  clickToPin: "Klicke an eine beliebige Stelle, um Feedback anzuheften",
17
18
  escToExit: "Esc zum Beenden",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "Sign in to comment",
13
13
  panelTitle: "Feedback",
14
14
  placeholder: "Leave feedback…",
15
+ commentingOn: "Commenting on {{name}}",
15
16
  emptyState: "No review comments yet.",
16
17
  clickToPin: "Click anywhere to pin feedback",
17
18
  escToExit: "Esc to exit",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "Inicia sesión para comentar",
13
13
  panelTitle: "Opiniones",
14
14
  placeholder: "Deja tus comentarios…",
15
+ commentingOn: "Comentando en {{name}}",
15
16
  emptyState: "Aún no hay comentarios de revisión.",
16
17
  clickToPin: "Haz clic en cualquier lugar para fijar un comentario",
17
18
  escToExit: "Pulsa Esc para salir",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "Connectez-vous pour commenter",
13
13
  panelTitle: "Retours",
14
14
  placeholder: "Laissez un commentaire…",
15
+ commentingOn: "Commentaire sur {{name}}",
15
16
  emptyState: "Aucun commentaire de révision pour le moment.",
16
17
  clickToPin: "Cliquez n’importe où pour épingler un commentaire",
17
18
  escToExit: "Appuyez sur Esc pour quitter",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "टिप्पणी करने के लिए साइन इन करें",
13
13
  panelTitle: "प्रतिक्रिया",
14
14
  placeholder: "अपनी प्रतिक्रिया दें…",
15
+ commentingOn: "{{name}} पर टिप्पणी",
15
16
  emptyState: "अभी तक कोई समीक्षा टिप्पणी नहीं है।",
16
17
  clickToPin: "प्रतिक्रिया पिन करने के लिए कहीं भी क्लिक करें",
17
18
  escToExit: "बाहर निकलने के लिए Esc दबाएँ",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "コメントするにはサインイン",
13
13
  panelTitle: "フィードバック",
14
14
  placeholder: "フィードバックを入力…",
15
+ commentingOn: "{{name}} にコメント",
15
16
  emptyState: "レビューコメントはまだありません。",
16
17
  clickToPin: "任意の場所をクリックしてフィードバックを固定",
17
18
  escToExit: "Esc で終了",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "댓글을 작성하려면 로그인",
13
13
  panelTitle: "피드백",
14
14
  placeholder: "피드백을 남겨 주세요…",
15
+ commentingOn: "{{name}}에 댓글 작성",
15
16
  emptyState: "아직 검토 댓글이 없습니다.",
16
17
  clickToPin: "아무 곳이나 클릭하여 피드백 고정",
17
18
  escToExit: "Esc를 눌러 종료",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "Entre para comentar",
13
13
  panelTitle: "Feedback",
14
14
  placeholder: "Deixe seu feedback…",
15
+ commentingOn: "Comentando em {{name}}",
15
16
  emptyState: "Ainda não há comentários de revisão.",
16
17
  clickToPin: "Clique em qualquer lugar para fixar o feedback",
17
18
  escToExit: "Pressione Esc para sair",
@@ -12,6 +12,7 @@ const messages = {
12
12
  signInToComment: "登录后评论",
13
13
  panelTitle: "反馈",
14
14
  placeholder: "留下反馈…",
15
+ commentingOn: "正在评论 {{name}}",
15
16
  emptyState: "暂无审阅评论。",
16
17
  clickToPin: "点击任意位置固定反馈",
17
18
  escToExit: "按 Esc 退出",
@@ -53,6 +53,7 @@ const messages = {
53
53
  signInToComment: "登入後評論",
54
54
  panelTitle: "意見回饋",
55
55
  placeholder: "留下意見…",
56
+ commentingOn: "正在評論 {{name}}",
56
57
  emptyState: "目前沒有審閱評論。",
57
58
  clickToPin: "點選任意位置以固定意見",
58
59
  escToExit: "按 Esc 退出",
@@ -483,6 +483,14 @@ export function shouldClearBridgeSelectionOnEmptyMarquee(args: {
483
483
  return args.resolvedCount === 0 && !args.additive;
484
484
  }
485
485
 
486
+ /** Clear element context only when a selected review thread changes screens. */
487
+ export function shouldClearSelectionForReviewThreadTarget(args: {
488
+ activeFileId?: string | null;
489
+ targetId?: string | null;
490
+ }): boolean {
491
+ return Boolean(args.targetId && args.targetId !== args.activeFileId);
492
+ }
493
+
486
494
  /**
487
495
  * PICK-RACE: MultiScreenCanvas's `onPick` prop is `(id: string) => void` — no
488
496
  * modifier/event info — even though a shift-click there already toggled a
@@ -102,6 +102,17 @@ export function shouldAutoEnableDrawOverlay(args: {
102
102
  );
103
103
  }
104
104
 
105
+ export type DesignBottomToolbarMode = "editor" | "commenter" | "hidden";
106
+
107
+ export function getDesignBottomToolbarMode(args: {
108
+ isSignedIn: boolean;
109
+ canEditDesign: boolean;
110
+ hasActiveFile: boolean;
111
+ }): DesignBottomToolbarMode {
112
+ if (!args.isSignedIn || !args.hasActiveFile) return "hidden";
113
+ return args.canEditDesign ? "editor" : "commenter";
114
+ }
115
+
105
116
  export function getSingleScreenCreationTool(args: {
106
117
  activeTool: DesignTool;
107
118
  viewMode: "single" | "overview";
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Reviewers can now pin comments from the canvas toolbar, target selected layers, and only see actions they can use.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-15
4
+ ---
5
+
6
+ Viewer comment controls now sit inside the read-only notice instead of floating over the canvas.
@@ -14,6 +14,54 @@ export interface ResolvedReviewAnchor {
14
14
  source: "node" | "point";
15
15
  }
16
16
 
17
+ export function createElementReviewAnchor(input: {
18
+ nodeId?: string | null;
19
+ rect?: { x: number; y: number; width: number; height: number } | null;
20
+ viewportWidth?: number | null;
21
+ viewportHeight?: number | null;
22
+ }): DesignReviewAnchor | null {
23
+ const nodeId = input.nodeId?.trim() ?? "";
24
+ const rect = input.rect;
25
+ const hasRect = Boolean(
26
+ rect &&
27
+ Number.isFinite(rect.x) &&
28
+ Number.isFinite(rect.y) &&
29
+ Number.isFinite(rect.width) &&
30
+ rect.width > 0 &&
31
+ Number.isFinite(rect.height) &&
32
+ rect.height > 0 &&
33
+ typeof input.viewportWidth === "number" &&
34
+ Number.isFinite(input.viewportWidth) &&
35
+ input.viewportWidth > 0 &&
36
+ typeof input.viewportHeight === "number" &&
37
+ Number.isFinite(input.viewportHeight) &&
38
+ input.viewportHeight > 0,
39
+ );
40
+ if (!nodeId && !hasRect) return null;
41
+
42
+ const point = hasRect
43
+ ? {
44
+ xPct:
45
+ finitePercentage(
46
+ (((rect?.x ?? 0) + (rect?.width ?? 0) / 2) /
47
+ (input.viewportWidth as number)) *
48
+ 100,
49
+ ) ?? 50,
50
+ yPct:
51
+ finitePercentage(
52
+ (((rect?.y ?? 0) + (rect?.height ?? 0) / 2) /
53
+ (input.viewportHeight as number)) *
54
+ 100,
55
+ ) ?? 50,
56
+ }
57
+ : { xPct: 50, yPct: 50 };
58
+
59
+ return {
60
+ ...(nodeId ? { nodeId } : {}),
61
+ point,
62
+ };
63
+ }
64
+
17
65
  function finitePercentage(value: unknown): number | null {
18
66
  if (typeof value !== "number" || !Number.isFinite(value)) return null;
19
67
  return Math.min(100, Math.max(0, value));
@@ -121,6 +121,23 @@ const drafts = await listAppState("compose-");
121
121
 
122
122
  The `send-email` action accepts an optional `attachments` array. Each entry must reference a file that was previously uploaded via the media-upload endpoint (`/api/media/upload`). Pass the server-side `filename` (the key returned by the upload endpoint, e.g. `abc123.pdf`), and optionally `originalName` (display name for the recipient) and `mimeType`. The attachment plumbing resolves the file from `data/uploads/` or from the configured file-storage URL recorded by the upload endpoint, then includes it as a MIME multipart attachment in the outgoing Gmail message. Do not store or paste attachment bytes, base64, or `data:` URLs in draft state/settings; files are never sent speculatively — only attach what the user has explicitly provided and confirmed.
123
123
 
124
+ ### Local files from an MCP host
125
+
126
+ A hosted Mail app cannot read a path on the MCP host's local filesystem. Use
127
+ `create-attachment-upload` with the file's display name. It returns a
128
+ five-minute, owner-bound `uploadUrl`, a `PUT` method, and the attachment handle
129
+ Mail expects. From the local host, upload the raw bytes directly:
130
+
131
+ ```bash
132
+ curl --fail-with-body --request PUT --header '<Authorization header>' --data-binary @/absolute/path/to/report.pdf '<uploadUrl>'
133
+ ```
134
+
135
+ Use the response's `attachment` object in the draft/send request. The upload
136
+ Authorization header is a short-lived bearer credential: do not log, persist,
137
+ reuse, or share it. Creating or consuming it does not send mail. Keep `send-email` behind its
138
+ normal explicit-intent and human-approval gate; for drafting, pass the handle
139
+ through `ask_app` or open the returned compose deep link.
140
+
124
141
  Example:
125
142
  ```json
126
143
  {
@@ -60,8 +60,9 @@ needed.
60
60
  - Never send mail unless the user explicitly asks to send. Draft or queue
61
61
  review by default. `send-email` has `needsApproval: true` — it is the
62
62
  canonical, intentionally rare use of the human-in-the-loop gate in this
63
- framework; the loop pauses for approval on every real send. Drafting and
64
- queueing are unaffected.
63
+ framework. An authenticated A2A caller may carry the user's exact chat
64
+ authorization for one matching send; otherwise the loop pauses for approval.
65
+ Drafting and queueing are unaffected.
65
66
  - When drafting, first read `get-mail-settings` for signature and writing
66
67
  style. Use `signature` exactly when present — draft-writing paths that build
67
68
  a `compose-*` entry (`manage-draft`, `open-queued-draft`) call
@@ -93,23 +94,24 @@ needed.
93
94
 
94
95
  ## Action Map
95
96
 
96
- | Action | Purpose |
97
- | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
98
- | `search-emails` / `list-emails` | Query across Gmail or local-fallback data by view/query. |
99
- | `get-email` / `get-thread` | Full body/metadata for one message or thread. |
100
- | `find-contact` | Resolve a name/partial address to a real email. |
101
- | `get-hubspot-contact` | CRM contact + deals + tickets by email (HubSpot only). |
102
- | `manage-draft` | Create/update/delete a `compose-{id}` draft (signature-aware). |
103
- | `send-email` | Real send. `needsApproval: true` always pauses for human approval. |
104
- | `queue-email-draft` / `list-queued-drafts` / `update-queued-draft` / `open-queued-draft` / `send-queued-drafts` | Teammate/Slack draft-review workflow see `draft-queue`. |
105
- | `mark-read` / `mark-thread-read` / `star-email` / `archive-email` / `unarchive-email` / `trash-email` / `untrash-email` / `move-email` | Per-message or per-thread state changes; most call `refresh-list` internally. |
106
- | `manage-gmail-filters` | Provider-native Gmail filters (create/replace/delete). |
107
- | `manage-automations` / `trigger-automations` | Natural-language inbox automation rules. |
108
- | `respond-calendar-invite` | Accept/decline/tentative an invite found in Mail. |
109
- | `get-mail-settings` / `update-mail-settings` / `import-gmail-signature` | Signature and writing-style settings. |
110
- | `manage-snippets` | List/create/update/delete saved reply snippets insertable from the compose slash menu. |
111
- | `get-tracking` | Open/click stats for a previously sent, tracked message. |
112
- | `provider-api-catalog` / `provider-api-docs` / `provider-api-request` | Raw Gmail, Calendar, or HubSpot API calls beyond the canned actions. |
97
+ | Action | Purpose |
98
+ | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
99
+ | `search-emails` / `list-emails` | Query across Gmail or local-fallback data by view/query. |
100
+ | `get-email` / `get-thread` | Full body/metadata for one message or thread. |
101
+ | `find-contact` | Resolve a name/partial address to a real email. |
102
+ | `get-hubspot-contact` | CRM contact + deals + tickets by email (HubSpot only). |
103
+ | `create-attachment-upload` | Mint a five-minute, owner-bound raw-byte upload URL for one local attachment. |
104
+ | `manage-draft` | Create/update/delete a `compose-{id}` draft (signature-aware). |
105
+ | `send-email` | Real send. `needsApproval: true`; exact authenticated chat grants run once, otherwise it pauses for approval. |
106
+ | `queue-email-draft` / `list-queued-drafts` / `update-queued-draft` / `open-queued-draft` / `send-queued-drafts` | Teammate/Slack draft-review workflow see `draft-queue`. |
107
+ | `mark-read` / `mark-thread-read` / `star-email` / `archive-email` / `unarchive-email` / `trash-email` / `untrash-email` / `move-email` | Per-message or per-thread state changes; most call `refresh-list` internally. |
108
+ | `manage-gmail-filters` | Provider-native Gmail filters (create/replace/delete). |
109
+ | `manage-automations` / `trigger-automations` | Natural-language inbox automation rules. |
110
+ | `respond-calendar-invite` | Accept/decline/tentative an invite found in Mail. |
111
+ | `get-mail-settings` / `update-mail-settings` / `import-gmail-signature` | Signature and writing-style settings. |
112
+ | `manage-snippets` | List/create/update/delete saved reply snippets insertable from the compose slash menu. |
113
+ | `get-tracking` | Open/click stats for a previously sent, tracked message. |
114
+ | `provider-api-catalog` / `provider-api-docs` / `provider-api-request` | Raw Gmail, Calendar, or HubSpot API calls beyond the canned actions. |
113
115
 
114
116
  ## Application State
115
117
 
@@ -0,0 +1,49 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import {
3
+ getAppProductionUrl,
4
+ getRequestUserEmail,
5
+ withConfiguredAppBasePath,
6
+ } from "@agent-native/core/server";
7
+ import { z } from "zod";
8
+
9
+ import { createAttachmentUploadTicket } from "../server/lib/attachment-upload-ticket.js";
10
+
11
+ const originalNameSchema = z
12
+ .string()
13
+ .trim()
14
+ .min(1)
15
+ .max(255)
16
+ .refine(
17
+ (value) => !/[\\/\0\r\n]/.test(value),
18
+ "Filename must not contain path separators or control characters",
19
+ );
20
+
21
+ export default defineAction({
22
+ description:
23
+ "Create a short-lived authenticated Mail upload URL for one local attachment. Upload the file bytes with HTTP PUT, then pass the returned attachment handle to ask_app so Mail can include it in a draft or approved send. Creating the URL does not send email.",
24
+ schema: z.object({
25
+ originalName: originalNameSchema.describe(
26
+ "Display filename, including its extension (for example, report.png)",
27
+ ),
28
+ }),
29
+ run: async ({ originalName }) => {
30
+ const ownerEmail = getRequestUserEmail();
31
+ if (!ownerEmail) throw new Error("Authentication required");
32
+ const ticket = await createAttachmentUploadTicket(ownerEmail, originalName);
33
+ const appUrl = withConfiguredAppBasePath(getAppProductionUrl());
34
+ const uploadUrl = `${appUrl}/api/media/attachment-upload/${encodeURIComponent(ticket.uploadId)}`;
35
+ return {
36
+ method: "PUT" as const,
37
+ uploadUrl,
38
+ headers: { Authorization: `Bearer ${ticket.token}` },
39
+ expiresAt: new Date(ticket.expiresAt).toISOString(),
40
+ maxBytes: 10 * 1024 * 1024,
41
+ attachment: {
42
+ filename: ticket.filename,
43
+ originalName: ticket.originalName,
44
+ mimeType: ticket.mimeType,
45
+ },
46
+ next: "PUT the raw local file bytes to uploadUrl with the returned Authorization header before passing attachment to ask_app. Do not paste base64 into chat or action arguments.",
47
+ };
48
+ },
49
+ });
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Connected agents can now securely upload local attachments, honor an exact send you explicitly authorized in chat, and otherwise pause real sends for browser approval.