@agent-native/core 0.114.4 → 0.114.6
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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +16 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/resources/ResourceTree.tsx +11 -5
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +141 -18
- package/corpus/templates/clips/.agents/skills/meetings/SKILL.md +11 -1
- package/corpus/templates/clips/AGENTS.md +14 -4
- package/corpus/templates/clips/actions/update-meeting.ts +13 -3
- package/corpus/templates/clips/actions/view-screen.ts +1 -0
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +96 -4
- package/corpus/templates/clips/app/i18n/en-US.ts +11 -0
- package/corpus/templates/clips/app/lib/public-meeting.ts +85 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +11 -1
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -7
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +241 -135
- package/corpus/templates/clips/changelog/2026-07-20-meeting-share-links-can-include-the-full-transcript-with-an-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-opening-zoom-meeting-launches-native-app.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-recording-retries-the-default-mac-microphone-when-a-saved-in.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-shared-clips-now-tell-agents-to-wait-while-uploads-and-trans.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +7 -11
- package/corpus/templates/clips/desktop/src/lib/meeting-join-url.ts +1 -33
- package/corpus/templates/clips/desktop/src/lib/open-meeting-join-url.ts +22 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +65 -2
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +2 -3
- package/corpus/templates/clips/server/db/schema.ts +3 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +31 -21
- package/corpus/templates/clips/server/plugins/auth.ts +1 -0
- package/corpus/templates/clips/server/plugins/db.ts +6 -0
- package/corpus/templates/clips/server/routes/api/agent-transcript.json.get.ts +13 -3
- package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +152 -0
- package/corpus/templates/clips/shared/agent-context.ts +60 -0
- package/corpus/templates/clips/shared/meeting-join-url.ts +31 -0
- package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +9 -1
- package/corpus/templates/content/AGENTS.md +4 -1
- package/corpus/templates/content/actions/_content-spaces.ts +6 -1
- package/corpus/templates/content/actions/_database-row-batch.ts +15 -2
- package/corpus/templates/content/actions/_database-utils.ts +83 -15
- package/corpus/templates/content/actions/add-database-item.ts +3 -0
- package/corpus/templates/content/actions/connect-local-folder-source.ts +2 -1
- package/corpus/templates/content/actions/delete-content-database.ts +12 -9
- package/corpus/templates/content/actions/delete-database-items.ts +3 -2
- package/corpus/templates/content/actions/delete-document.ts +233 -6
- package/corpus/templates/content/actions/duplicate-database-item.ts +1 -0
- package/corpus/templates/content/actions/get-document.ts +4 -1
- package/corpus/templates/content/actions/list-content-databases.ts +1 -0
- package/corpus/templates/content/actions/list-documents.ts +1 -0
- package/corpus/templates/content/actions/list-trashed-content-databases.ts +8 -2
- package/corpus/templates/content/actions/list-trashed-documents.ts +50 -0
- package/corpus/templates/content/actions/permanently-delete-document.ts +28 -0
- package/corpus/templates/content/actions/pull-document.ts +15 -2
- package/corpus/templates/content/actions/restore-content-database.ts +48 -11
- package/corpus/templates/content/actions/restore-document.ts +28 -0
- package/corpus/templates/content/actions/search-documents.ts +2 -1
- package/corpus/templates/content/actions/view-screen.ts +2 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +149 -55
- package/corpus/templates/content/app/components/editor/database/GalleryView.tsx +7 -2
- package/corpus/templates/content/app/components/editor/database/ListView.tsx +7 -2
- package/corpus/templates/content/app/components/editor/database/TimelineView.tsx +3 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +26 -5
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +243 -163
- package/corpus/templates/content/app/components/sidebar/WorkspaceSourceMenu.tsx +154 -0
- package/corpus/templates/content/app/components/sidebar/select-content-space.ts +11 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +6 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +57 -0
- package/corpus/templates/content/app/i18n-data.ts +83 -2
- package/corpus/templates/content/app/routes/_app.local-files.tsx +8 -1
- package/corpus/templates/content/changelog/2026-07-19-pages-now-move-to-a-reversible-trash-and-the-organization-pi.md +6 -0
- package/corpus/templates/content/changelog/2026-07-20-trash-actions-now-preserve-independent-archived-pages-enforc.md +6 -0
- package/corpus/templates/content/changelog/2026-07-20-workspace-toggles-now-stay-independently-open-or-closed-and-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-20-workspaces-can-be-added-from-a-blank-workspace-or-a-connecte.md +6 -0
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +3 -0
- package/corpus/templates/content/server/db/schema.ts +2 -0
- package/corpus/templates/content/server/lib/public-documents.ts +8 -2
- package/corpus/templates/content/server/plugins/db.ts +52 -0
- package/corpus/templates/content/server/routes/api/document-agent-context.json.get.ts +2 -2
- package/corpus/templates/content/shared/api.ts +10 -0
- package/corpus/toolkit/CHANGELOG.md +6 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/ui/progress.tsx +3 -2
- package/dist/client/resources/ResourceTree.d.ts +3 -1
- package/dist/client/resources/ResourceTree.d.ts.map +1 -1
- package/dist/client/resources/ResourceTree.js +4 -4
- package/dist/client/resources/ResourceTree.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +55 -6
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +3 -3
- package/src/client/resources/ResourceTree.tsx +11 -5
- package/src/client/resources/ResourcesPanel.tsx +141 -18
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.114.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6d96437: Add clear creation actions to empty resource views and improve collaboration usage feedback.
|
|
8
|
+
- Updated dependencies [6d96437]
|
|
9
|
+
- @agent-native/toolkit@0.8.1
|
|
10
|
+
|
|
11
|
+
## 0.114.5
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 570be31: Skip visual recap publishing when the authoring agent does not produce recap source.
|
|
16
|
+
- Updated dependencies [570be31]
|
|
17
|
+
- @agent-native/recap-cli@0.4.5
|
|
18
|
+
|
|
3
19
|
## 0.114.4
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.114.
|
|
3
|
+
"version": "0.114.6",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -120,6 +120,8 @@ export interface ResourceTreeProps {
|
|
|
120
120
|
headingHint?: React.ReactNode;
|
|
121
121
|
/** Scope-specific action shown beside the section heading. */
|
|
122
122
|
sectionAction?: React.ReactNode;
|
|
123
|
+
/** Scope-specific action shown beneath an empty collection message. */
|
|
124
|
+
emptyStateAction?: React.ReactNode;
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
interface CreatingState {
|
|
@@ -638,6 +640,7 @@ export function ResourceTree({
|
|
|
638
640
|
readOnly = false,
|
|
639
641
|
headingHint,
|
|
640
642
|
sectionAction,
|
|
643
|
+
emptyStateAction,
|
|
641
644
|
}: ResourceTreeProps) {
|
|
642
645
|
const [expanded, setExpanded] = useState<Set<string>>(() => new Set());
|
|
643
646
|
const [creating, setCreating] = useState<CreatingState | null>(null);
|
|
@@ -848,11 +851,14 @@ export function ResourceTree({
|
|
|
848
851
|
{isEmpty && !creating && !isLoading && (
|
|
849
852
|
<div className="flex items-start gap-2 border-y border-border/60 px-2 py-4">
|
|
850
853
|
<IconFile className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />
|
|
851
|
-
<
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
854
|
+
<div className="flex flex-col items-start gap-2">
|
|
855
|
+
<p className="text-xs text-muted-foreground">
|
|
856
|
+
{variant === "collection"
|
|
857
|
+
? "No resources in this collection yet"
|
|
858
|
+
: "No files yet"}
|
|
859
|
+
</p>
|
|
860
|
+
{emptyStateAction}
|
|
861
|
+
</div>
|
|
856
862
|
</div>
|
|
857
863
|
)}
|
|
858
864
|
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Button } from "@agent-native/toolkit/ui/button";
|
|
1
2
|
import {
|
|
2
3
|
IconPlus,
|
|
3
4
|
IconUpload,
|
|
@@ -82,6 +83,42 @@ import {
|
|
|
82
83
|
const WORKSPACE_DOCS_URL = "https://agent-native.com/docs/workspace";
|
|
83
84
|
const LOCAL_WORKSPACE_RESOURCE_METADATA_SOURCE = "local-workspace-resource";
|
|
84
85
|
|
|
86
|
+
const EMPTY_RESOURCE_ACTION_LABELS: Record<ResourceView, string> = {
|
|
87
|
+
files: "Add file",
|
|
88
|
+
instructions: "Add instructions",
|
|
89
|
+
agents: "Add agent",
|
|
90
|
+
memory: "Add memory",
|
|
91
|
+
skills: "Add skill",
|
|
92
|
+
learnings: "Add learning",
|
|
93
|
+
"remote-agents": "Add remote agent",
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const EMPTY_RESOURCE_SEEDS: Partial<
|
|
97
|
+
Record<ResourceView, { path: string; content: string; mimeType?: string }>
|
|
98
|
+
> = {
|
|
99
|
+
instructions: {
|
|
100
|
+
path: "AGENTS.md",
|
|
101
|
+
content: "# Agent Instructions\n\n",
|
|
102
|
+
mimeType: "text/markdown",
|
|
103
|
+
},
|
|
104
|
+
memory: {
|
|
105
|
+
path: "memory/MEMORY.md",
|
|
106
|
+
content: "# Memory\n\n",
|
|
107
|
+
mimeType: "text/markdown",
|
|
108
|
+
},
|
|
109
|
+
learnings: {
|
|
110
|
+
path: "LEARNINGS.md",
|
|
111
|
+
content: "# Learnings\n\n",
|
|
112
|
+
mimeType: "text/markdown",
|
|
113
|
+
},
|
|
114
|
+
"remote-agents": {
|
|
115
|
+
path: "remote-agents/new-agent.json",
|
|
116
|
+
content:
|
|
117
|
+
'{\n "id": "new-agent",\n "name": "New agent",\n "description": "",\n "url": "",\n "color": "#6B7280"\n}\n',
|
|
118
|
+
mimeType: "application/json",
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
85
122
|
export type ResourceView =
|
|
86
123
|
| "files"
|
|
87
124
|
| "instructions"
|
|
@@ -228,6 +265,9 @@ function CreateMenu({
|
|
|
228
265
|
onCreated,
|
|
229
266
|
showToast,
|
|
230
267
|
mcpIntegrations,
|
|
268
|
+
triggerVariant = "icon",
|
|
269
|
+
triggerLabel,
|
|
270
|
+
initialView = "menu",
|
|
231
271
|
}: {
|
|
232
272
|
scope: ResourceScope;
|
|
233
273
|
resourceFilter?: ResourceView;
|
|
@@ -257,6 +297,9 @@ function CreateMenu({
|
|
|
257
297
|
message: string,
|
|
258
298
|
opts?: { resourceId?: string; durationMs?: number },
|
|
259
299
|
) => void;
|
|
300
|
+
triggerVariant?: "icon" | "outline";
|
|
301
|
+
triggerLabel?: string;
|
|
302
|
+
initialView?: CreateMenuView;
|
|
260
303
|
}) {
|
|
261
304
|
const t = useT();
|
|
262
305
|
const [open, setOpen] = useState(false);
|
|
@@ -313,7 +356,7 @@ function CreateMenu({
|
|
|
313
356
|
|
|
314
357
|
useEffect(() => {
|
|
315
358
|
if (open) {
|
|
316
|
-
setView(
|
|
359
|
+
setView(initialView);
|
|
317
360
|
setValue("");
|
|
318
361
|
setAgentName("");
|
|
319
362
|
setAgentDescription("");
|
|
@@ -326,7 +369,7 @@ function CreateMenu({
|
|
|
326
369
|
setSkillUploadFileName("");
|
|
327
370
|
setSkillFlyoutOpen(false);
|
|
328
371
|
}
|
|
329
|
-
}, [open]);
|
|
372
|
+
}, [initialView, open]);
|
|
330
373
|
|
|
331
374
|
useEffect(() => {
|
|
332
375
|
if (view !== "menu" && view !== "agent-form") {
|
|
@@ -642,22 +685,36 @@ The result should be a reusable agent profile, not a one-off task response.`,
|
|
|
642
685
|
e.target.value = "";
|
|
643
686
|
}}
|
|
644
687
|
/>
|
|
645
|
-
|
|
646
|
-
<
|
|
647
|
-
<
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
688
|
+
{triggerVariant === "outline" ? (
|
|
689
|
+
<PopoverTrigger asChild>
|
|
690
|
+
<Button
|
|
691
|
+
type="button"
|
|
692
|
+
variant="outline"
|
|
693
|
+
size="sm"
|
|
694
|
+
className="h-7 gap-1.5 px-2.5 text-xs"
|
|
695
|
+
>
|
|
696
|
+
<IconPlus className="size-3.5" />
|
|
697
|
+
{triggerLabel ?? "Add resource"}
|
|
698
|
+
</Button>
|
|
699
|
+
</PopoverTrigger>
|
|
700
|
+
) : (
|
|
701
|
+
<Tooltip>
|
|
702
|
+
<TooltipTrigger asChild>
|
|
703
|
+
<PopoverTrigger asChild>
|
|
704
|
+
<button
|
|
705
|
+
type="button"
|
|
706
|
+
className={cn(
|
|
707
|
+
"flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent/50",
|
|
708
|
+
open && "bg-accent/50 text-foreground",
|
|
709
|
+
)}
|
|
710
|
+
>
|
|
711
|
+
<IconPlus className="h-3.5 w-3.5" />
|
|
712
|
+
</button>
|
|
713
|
+
</PopoverTrigger>
|
|
714
|
+
</TooltipTrigger>
|
|
715
|
+
<TooltipContent>Create new...</TooltipContent>
|
|
716
|
+
</Tooltip>
|
|
717
|
+
)}
|
|
661
718
|
<PopoverContent
|
|
662
719
|
align="end"
|
|
663
720
|
sideOffset={6}
|
|
@@ -1522,6 +1579,70 @@ export function ResourcesPanel({
|
|
|
1522
1579
|
);
|
|
1523
1580
|
};
|
|
1524
1581
|
|
|
1582
|
+
const renderEmptyStateAction = (targetScope: ResourceScope) => {
|
|
1583
|
+
if (targetScope === "shared" && !canEditOrg) return null;
|
|
1584
|
+
|
|
1585
|
+
const label = resourceFilter
|
|
1586
|
+
? EMPTY_RESOURCE_ACTION_LABELS[resourceFilter]
|
|
1587
|
+
: "Add resource";
|
|
1588
|
+
|
|
1589
|
+
if (
|
|
1590
|
+
!resourceFilter ||
|
|
1591
|
+
resourceFilter === "files" ||
|
|
1592
|
+
resourceFilter === "agents" ||
|
|
1593
|
+
resourceFilter === "skills"
|
|
1594
|
+
) {
|
|
1595
|
+
return (
|
|
1596
|
+
<CreateMenu
|
|
1597
|
+
scope={targetScope}
|
|
1598
|
+
resourceFilter={resourceFilter}
|
|
1599
|
+
onCreateFile={(name) => handleCreateFromToolbar(targetScope, name)}
|
|
1600
|
+
onCreateResource={(path, content, mimeType, opts) =>
|
|
1601
|
+
handleCreateResourceFromToolbar(
|
|
1602
|
+
targetScope,
|
|
1603
|
+
path,
|
|
1604
|
+
content,
|
|
1605
|
+
mimeType,
|
|
1606
|
+
opts,
|
|
1607
|
+
)
|
|
1608
|
+
}
|
|
1609
|
+
onCreateMcpServer={handleCreateMcpServer}
|
|
1610
|
+
canCreateOrgMcp={canCreateOrgMcp}
|
|
1611
|
+
hasOrg={hasOrgForMcp}
|
|
1612
|
+
showToast={showToast}
|
|
1613
|
+
mcpIntegrations={mcpIntegrations}
|
|
1614
|
+
triggerVariant="outline"
|
|
1615
|
+
triggerLabel={label}
|
|
1616
|
+
initialView={resourceFilter === "files" ? "file" : "menu"}
|
|
1617
|
+
/>
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
const seed = EMPTY_RESOURCE_SEEDS[resourceFilter];
|
|
1622
|
+
if (!seed) return null;
|
|
1623
|
+
|
|
1624
|
+
return (
|
|
1625
|
+
<Button
|
|
1626
|
+
type="button"
|
|
1627
|
+
variant="outline"
|
|
1628
|
+
size="sm"
|
|
1629
|
+
className="h-7 gap-1.5 px-2.5 text-xs"
|
|
1630
|
+
disabled={createResource.isPending}
|
|
1631
|
+
onClick={() =>
|
|
1632
|
+
handleCreateResourceFromToolbar(
|
|
1633
|
+
targetScope,
|
|
1634
|
+
seed.path,
|
|
1635
|
+
seed.content,
|
|
1636
|
+
seed.mimeType,
|
|
1637
|
+
)
|
|
1638
|
+
}
|
|
1639
|
+
>
|
|
1640
|
+
<IconPlus className="size-3.5" />
|
|
1641
|
+
{label}
|
|
1642
|
+
</Button>
|
|
1643
|
+
);
|
|
1644
|
+
};
|
|
1645
|
+
|
|
1525
1646
|
return (
|
|
1526
1647
|
<div
|
|
1527
1648
|
className={cn(
|
|
@@ -1881,6 +2002,7 @@ export function ResourcesPanel({
|
|
|
1881
2002
|
title="Personal"
|
|
1882
2003
|
titleTooltip="Files visible only to you"
|
|
1883
2004
|
sectionAction={renderScopeCreateMenu("personal")}
|
|
2005
|
+
emptyStateAction={renderEmptyStateAction("personal")}
|
|
1884
2006
|
/>
|
|
1885
2007
|
)}
|
|
1886
2008
|
{showSharedTree && (
|
|
@@ -1915,6 +2037,7 @@ export function ResourcesPanel({
|
|
|
1915
2037
|
readOnly={!canEditOrg}
|
|
1916
2038
|
headingHint={!canEditOrg ? "Read only" : undefined}
|
|
1917
2039
|
sectionAction={renderScopeCreateMenu("shared")}
|
|
2040
|
+
emptyStateAction={renderEmptyStateAction("shared")}
|
|
1918
2041
|
/>
|
|
1919
2042
|
)}
|
|
1920
2043
|
</div>
|
|
@@ -78,7 +78,7 @@ When meetings are enabled, the Clips desktop app also watches for native Zoom (`
|
|
|
78
78
|
| `list-meetings` | Upcoming + past, scoped via `accessFilter`; reads connected Google Calendar live |
|
|
79
79
|
| `get-meeting` | One meeting + participants + segments + notes |
|
|
80
80
|
| `create-meeting` | Create a meeting row (`source`: `calendar` / `adhoc` / `manual`); desktop adhoc Zoom/Teams detection passes `source: "adhoc"` |
|
|
81
|
-
| `update-meeting` | Inline title
|
|
81
|
+
| `update-meeting` | Inline title/notes edits; owners/admins can opt the full transcript into or out of the meeting share link with `shareTranscript` |
|
|
82
82
|
| `delete-meeting` | Soft-delete a meeting from the visible list; linked recordings and calendar events stay intact |
|
|
83
83
|
| `start-meeting-recording` | Begin native macOS transcript stream + create the linked recording |
|
|
84
84
|
| `stop-meeting-recording` | End the active capture |
|
|
@@ -91,6 +91,15 @@ When meetings are enabled, the Clips desktop app also watches for native Zoom (`
|
|
|
91
91
|
|
|
92
92
|
All actions go through `accessFilter` / `assertAccess`. AI work delegates via `sendToAgentChat` per the `delegate-to-agent` skill — never inline LLM calls.
|
|
93
93
|
|
|
94
|
+
## Sharing meeting notes and transcripts
|
|
95
|
+
|
|
96
|
+
Meeting share links always include generated notes: the summary, key points,
|
|
97
|
+
and action items. Full transcripts are more sensitive and stay excluded by
|
|
98
|
+
default. Owners and share admins can call `update-meeting` with
|
|
99
|
+
`shareTranscript=true|false` or use the Share popover's transcript switch.
|
|
100
|
+
The setting controls only the meeting share payload; it must not change the
|
|
101
|
+
linked recording's visibility or expose recording media and comments.
|
|
102
|
+
|
|
94
103
|
## Cleanup credential order
|
|
95
104
|
|
|
96
105
|
The `cleanup-transcript` action resolves credentials in this order — **always lead with Builder.io Connect** when explaining options to the user:
|
|
@@ -121,6 +130,7 @@ The app exposes `view`, `meetingId`, and `dictationId` so the agent always knows
|
|
|
121
130
|
"scheduledStart": "...",
|
|
122
131
|
"scheduledEnd": "...",
|
|
123
132
|
"transcriptStatus": "ready",
|
|
133
|
+
"shareTranscript": false,
|
|
124
134
|
"participants": [{ "email": "alice@ex.com", "name": "Alice" }, ...],
|
|
125
135
|
"actionItems": [{ "assigneeEmail": "alice@ex.com", "text": "..." }, ...],
|
|
126
136
|
"hasRecording": true,
|
|
@@ -102,6 +102,11 @@ ladder.
|
|
|
102
102
|
sharing/status boundary.
|
|
103
103
|
- Use framework sharing actions for recordings. Password and expiry are extra
|
|
104
104
|
controls on top of visibility/share grants.
|
|
105
|
+
- Meeting share links include the summary, key points, and action items. The
|
|
106
|
+
full transcript is an explicit, default-off setting: call `update-meeting`
|
|
107
|
+
with `shareTranscript=true|false` only when the owner or a share admin asks
|
|
108
|
+
to change what the meeting link exposes. This does not change the linked
|
|
109
|
+
recording's visibility or expose its media.
|
|
105
110
|
- Use `list-recordings --view=shared` for the current user's "Shared with me"
|
|
106
111
|
collection. It returns recordings admitted by sharing access that are owned
|
|
107
112
|
by someone else; public-link-only clips remain out of this list.
|
|
@@ -111,10 +116,15 @@ ladder.
|
|
|
111
116
|
segments; `/api/agent-frame.jpg?id=<recordingId>&atMs=<ms>` for a screen
|
|
112
117
|
frame at a timestamp. Password-protected clips require the password once to
|
|
113
118
|
mint a short-lived token returned inside agent-context links.
|
|
114
|
-
- If public agent context
|
|
115
|
-
`"
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
- If a public agent discovery/context/transcript payload reports
|
|
120
|
+
`agentReadiness.state` as `"preparing"` (the clip is `"uploading"` or
|
|
121
|
+
`"processing"`), wait 15 seconds and retry `agentContextUrl`; do not open the
|
|
122
|
+
share page, fetch frames, or draw conclusions until the recording status is
|
|
123
|
+
`"ready"`.
|
|
124
|
+
If `transcript.status` is `"pending"` after the clip is ready, wait 15-30
|
|
125
|
+
seconds and retry the context/transcript URL a few times, especially for long
|
|
126
|
+
recordings. Do not pivot straight to frames or tell the user there is no
|
|
127
|
+
transcript until the retry budget is exhausted.
|
|
118
128
|
- If transcription failed because Builder transcription credits are exhausted,
|
|
119
129
|
tell the user that clearly and point them to Builder.io credits/upgrade or a
|
|
120
130
|
Groq key for backup speech-to-text. Generic OpenAI or Anthropic chat keys do
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Update
|
|
2
|
+
* Update meeting content or owner/admin-managed sharing settings.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { defineAction } from "@agent-native/core";
|
|
@@ -9,10 +9,11 @@ import { eq } from "drizzle-orm";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
|
|
11
11
|
import { getDb, schema } from "../server/db/index.js";
|
|
12
|
+
import { booleanParam } from "./lib/cli-params.js";
|
|
12
13
|
|
|
13
14
|
export default defineAction({
|
|
14
15
|
description:
|
|
15
|
-
"Partially update a meeting
|
|
16
|
+
"Partially update a meeting's content. Owners and share admins can also control visibility and whether share links include the transcript.",
|
|
16
17
|
schema: z.object({
|
|
17
18
|
id: z.string().describe("Meeting id"),
|
|
18
19
|
title: z.string().optional(),
|
|
@@ -41,10 +42,17 @@ export default defineAction({
|
|
|
41
42
|
.optional()
|
|
42
43
|
.describe("Replace the action item set on the meetings row JSON"),
|
|
43
44
|
transcriptStatus: z.enum(["idle", "pending", "ready", "failed"]).optional(),
|
|
45
|
+
shareTranscript: booleanParam
|
|
46
|
+
.optional()
|
|
47
|
+
.describe(
|
|
48
|
+
"Include the linked recording transcript on meeting share pages",
|
|
49
|
+
),
|
|
44
50
|
visibility: z.enum(["private", "org", "public"]).optional(),
|
|
45
51
|
}),
|
|
46
52
|
run: async (args) => {
|
|
47
|
-
|
|
53
|
+
const updatesSharing =
|
|
54
|
+
args.shareTranscript !== undefined || args.visibility !== undefined;
|
|
55
|
+
await assertAccess("meeting", args.id, updatesSharing ? "admin" : "editor");
|
|
48
56
|
const db = getDb();
|
|
49
57
|
|
|
50
58
|
const patch: Record<string, unknown> = {
|
|
@@ -67,6 +75,8 @@ export default defineAction({
|
|
|
67
75
|
if (args.actionItems)
|
|
68
76
|
patch.actionItemsJson = JSON.stringify(args.actionItems);
|
|
69
77
|
if (args.transcriptStatus) patch.transcriptStatus = args.transcriptStatus;
|
|
78
|
+
if (args.shareTranscript !== undefined)
|
|
79
|
+
patch.shareTranscript = args.shareTranscript;
|
|
70
80
|
if (args.visibility) patch.visibility = args.visibility;
|
|
71
81
|
|
|
72
82
|
await db
|
|
@@ -414,6 +414,7 @@ async function fetchMeetingDetail(meetingId: string) {
|
|
|
414
414
|
source: meeting.source,
|
|
415
415
|
userNotesMd: meeting.userNotesMd,
|
|
416
416
|
transcriptStatus: meeting.transcriptStatus,
|
|
417
|
+
shareTranscript: Boolean(meeting.shareTranscript),
|
|
417
418
|
summaryMd: meeting.summaryMd,
|
|
418
419
|
bullets: safeJsonArray<{ text: string }>(meeting.bulletsJson),
|
|
419
420
|
},
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { appPath } from "@agent-native/core/client/api-path";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useActionMutation,
|
|
4
|
+
useActionQuery,
|
|
5
|
+
} from "@agent-native/core/client/hooks";
|
|
3
6
|
import { useT } from "@agent-native/core/client/i18n";
|
|
4
|
-
import { IconLink, IconMail } from "@tabler/icons-react";
|
|
5
|
-
import { useMemo, type ReactNode } from "react";
|
|
7
|
+
import { IconCheck, IconLink, IconMail } from "@tabler/icons-react";
|
|
8
|
+
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
|
6
9
|
import { toast } from "sonner";
|
|
7
10
|
|
|
8
11
|
import {
|
|
@@ -22,17 +25,22 @@ import {
|
|
|
22
25
|
PopoverContent,
|
|
23
26
|
PopoverTrigger,
|
|
24
27
|
} from "@/components/ui/popover";
|
|
28
|
+
import { Switch } from "@/components/ui/switch";
|
|
25
29
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
26
30
|
|
|
27
31
|
export interface ShareMeetingPopoverProps {
|
|
28
32
|
meetingId: string;
|
|
29
33
|
meetingTitle?: string;
|
|
34
|
+
shareTranscript: boolean;
|
|
35
|
+
transcriptReady: boolean;
|
|
30
36
|
children: ReactNode;
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
export function ShareMeetingPopover({
|
|
34
40
|
meetingId,
|
|
35
41
|
meetingTitle,
|
|
42
|
+
shareTranscript,
|
|
43
|
+
transcriptReady,
|
|
36
44
|
children,
|
|
37
45
|
}: ShareMeetingPopoverProps) {
|
|
38
46
|
return (
|
|
@@ -40,11 +48,13 @@ export function ShareMeetingPopover({
|
|
|
40
48
|
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
|
41
49
|
<PopoverContent
|
|
42
50
|
align="end"
|
|
43
|
-
className="w-[440px] max-w-[calc(100vw-1rem)] overflow-
|
|
51
|
+
className="max-h-[calc(100vh-1rem)] w-[440px] max-w-[calc(100vw-1rem)] overflow-y-auto border-border p-0"
|
|
44
52
|
>
|
|
45
53
|
<ShareMeetingContent
|
|
46
54
|
meetingId={meetingId}
|
|
47
55
|
meetingTitle={meetingTitle}
|
|
56
|
+
shareTranscript={shareTranscript}
|
|
57
|
+
transcriptReady={transcriptReady}
|
|
48
58
|
/>
|
|
49
59
|
</PopoverContent>
|
|
50
60
|
</Popover>
|
|
@@ -54,9 +64,13 @@ export function ShareMeetingPopover({
|
|
|
54
64
|
function ShareMeetingContent({
|
|
55
65
|
meetingId,
|
|
56
66
|
meetingTitle,
|
|
67
|
+
shareTranscript,
|
|
68
|
+
transcriptReady,
|
|
57
69
|
}: {
|
|
58
70
|
meetingId: string;
|
|
59
71
|
meetingTitle?: string;
|
|
72
|
+
shareTranscript: boolean;
|
|
73
|
+
transcriptReady: boolean;
|
|
60
74
|
}) {
|
|
61
75
|
const t = useT();
|
|
62
76
|
const shareUrl = useMemo(
|
|
@@ -97,6 +111,8 @@ function ShareMeetingContent({
|
|
|
97
111
|
shareUrl={shareUrl}
|
|
98
112
|
sharesQuery={sharesQuery}
|
|
99
113
|
canManage={canManage}
|
|
114
|
+
shareTranscript={shareTranscript}
|
|
115
|
+
transcriptReady={transcriptReady}
|
|
100
116
|
/>
|
|
101
117
|
</TabsContent>
|
|
102
118
|
|
|
@@ -127,13 +143,22 @@ function LinkTab({
|
|
|
127
143
|
shareUrl,
|
|
128
144
|
sharesQuery,
|
|
129
145
|
canManage,
|
|
146
|
+
shareTranscript,
|
|
147
|
+
transcriptReady,
|
|
130
148
|
}: {
|
|
131
149
|
meetingId: string;
|
|
132
150
|
shareUrl: string;
|
|
133
151
|
sharesQuery: SharesQuery;
|
|
134
152
|
canManage: boolean;
|
|
153
|
+
shareTranscript: boolean;
|
|
154
|
+
transcriptReady: boolean;
|
|
135
155
|
}) {
|
|
136
156
|
const t = useT();
|
|
157
|
+
const updateMeeting = useActionMutation<
|
|
158
|
+
unknown,
|
|
159
|
+
{ id: string; shareTranscript: boolean }
|
|
160
|
+
>("update-meeting");
|
|
161
|
+
const [includeTranscript, setIncludeTranscript] = useState(shareTranscript);
|
|
137
162
|
const { setResourceVisibility, isPending } = useResourceVisibilityMutation(
|
|
138
163
|
"meeting",
|
|
139
164
|
meetingId,
|
|
@@ -144,6 +169,28 @@ function LinkTab({
|
|
|
144
169
|
(data?.visibility as Visibility | null) ?? "private";
|
|
145
170
|
const isPublic = visibility === "public";
|
|
146
171
|
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
setIncludeTranscript(shareTranscript);
|
|
174
|
+
}, [shareTranscript]);
|
|
175
|
+
|
|
176
|
+
const handleTranscriptSharingChange = (next: boolean) => {
|
|
177
|
+
const previous = includeTranscript;
|
|
178
|
+
setIncludeTranscript(next);
|
|
179
|
+
updateMeeting.mutate(
|
|
180
|
+
{ id: meetingId, shareTranscript: next },
|
|
181
|
+
{
|
|
182
|
+
onError: (error: unknown) => {
|
|
183
|
+
setIncludeTranscript(previous);
|
|
184
|
+
toast.error(
|
|
185
|
+
error instanceof Error
|
|
186
|
+
? error.message
|
|
187
|
+
: t("shareMeeting.updateTranscriptSharingFailed"),
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
};
|
|
193
|
+
|
|
147
194
|
return (
|
|
148
195
|
<div className="space-y-4">
|
|
149
196
|
<GeneralAccessSelect
|
|
@@ -153,6 +200,51 @@ function LinkTab({
|
|
|
153
200
|
onChange={(next) => setResourceVisibility(next)}
|
|
154
201
|
/>
|
|
155
202
|
|
|
203
|
+
<div>
|
|
204
|
+
<div className="mb-2 text-xs font-semibold">
|
|
205
|
+
{t("shareMeeting.sharedContent")}
|
|
206
|
+
</div>
|
|
207
|
+
<div className="rounded-md border border-border">
|
|
208
|
+
<div className="flex items-center gap-3 border-b border-border px-3 py-2.5">
|
|
209
|
+
<span
|
|
210
|
+
aria-hidden
|
|
211
|
+
className="inline-flex size-5 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground"
|
|
212
|
+
>
|
|
213
|
+
<IconCheck size={12} strokeWidth={2.5} />
|
|
214
|
+
</span>
|
|
215
|
+
<span className="text-sm">{t("shareMeeting.summaryIncluded")}</span>
|
|
216
|
+
</div>
|
|
217
|
+
<div className="flex items-start justify-between gap-4 px-3 py-2.5">
|
|
218
|
+
<div className="min-w-0">
|
|
219
|
+
<label
|
|
220
|
+
htmlFor={`meeting-share-transcript-${meetingId}`}
|
|
221
|
+
className="text-sm font-medium"
|
|
222
|
+
>
|
|
223
|
+
{t("shareMeeting.includeTranscript")}
|
|
224
|
+
</label>
|
|
225
|
+
<p
|
|
226
|
+
id={`meeting-share-transcript-description-${meetingId}`}
|
|
227
|
+
className="mt-0.5 text-xs text-muted-foreground"
|
|
228
|
+
>
|
|
229
|
+
{transcriptReady
|
|
230
|
+
? t("shareMeeting.includeTranscriptDescription")
|
|
231
|
+
: t("shareMeeting.transcriptUnavailable")}
|
|
232
|
+
</p>
|
|
233
|
+
</div>
|
|
234
|
+
<Switch
|
|
235
|
+
id={`meeting-share-transcript-${meetingId}`}
|
|
236
|
+
checked={includeTranscript}
|
|
237
|
+
onCheckedChange={handleTranscriptSharingChange}
|
|
238
|
+
disabled={
|
|
239
|
+
!canManage || !transcriptReady || updateMeeting.isPending
|
|
240
|
+
}
|
|
241
|
+
aria-describedby={`meeting-share-transcript-description-${meetingId}`}
|
|
242
|
+
className="mt-0.5 shrink-0"
|
|
243
|
+
/>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
156
248
|
<CopyField label={t("clipsFinalRaw.shareLink")} value={shareUrl} />
|
|
157
249
|
|
|
158
250
|
{!isPublic && canManage ? (
|
|
@@ -927,6 +927,17 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
927
927
|
summary: "Summary",
|
|
928
928
|
keyPoints: "Key points",
|
|
929
929
|
actionItems: "Action items",
|
|
930
|
+
sharedContent: "Shared content",
|
|
931
|
+
summaryIncluded: "Summary, key points, and action items",
|
|
932
|
+
includeTranscript: "Include full transcript",
|
|
933
|
+
includeTranscriptDescription:
|
|
934
|
+
"Anyone with access to this meeting can read the complete transcript.",
|
|
935
|
+
transcriptUnavailable: "The transcript isn't ready yet.",
|
|
936
|
+
transcript: "Transcript",
|
|
937
|
+
copyTranscript: "Copy transcript",
|
|
938
|
+
transcriptCopied: "Transcript copied",
|
|
939
|
+
copyTranscriptFailed: "Couldn't copy the transcript",
|
|
940
|
+
updateTranscriptSharingFailed: "Couldn't update transcript sharing",
|
|
930
941
|
},
|
|
931
942
|
deleteRecordingMenu: {
|
|
932
943
|
movedToTrash: "Clip moved to trash",
|