@agent-native/core 0.122.0 → 0.122.2
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/README.md +5 -25
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +15 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +57 -4
- package/corpus/core/src/agent/production-agent.ts +65 -1
- package/corpus/core/src/agent/run-store.ts +61 -1
- package/corpus/core/src/client/settings/SettingsPanel.tsx +9 -9
- package/corpus/core/src/client/settings/SettingsTabsPage.tsx +12 -2
- package/corpus/core/src/extensions/url-safety.ts +67 -4
- package/corpus/core/src/server/builder-design-systems.ts +1 -1
- package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +0 -7
- package/corpus/core/src/templates/default/app/root.tsx +1 -1
- package/corpus/core/src/templates/default/public/manifest.json +2 -2
- package/corpus/core/src/vite/client.ts +64 -3
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +15 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -23
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-panel-window.ts +3 -3
- package/corpus/templates/analytics/app/routes/_index.tsx +1 -1
- package/corpus/templates/analytics/changelog/2026-07-25-analytics-dashboards-and-scheduled-reports-are-faster-and-mo.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/analytics/package.json +1 -1
- package/corpus/templates/analytics/public/manifest.json +1 -1
- package/corpus/templates/analytics/server/db/schema.ts +12 -0
- package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +8 -4
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +136 -5
- package/corpus/templates/analytics/server/lib/dashboard-time-scope.ts +250 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +167 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +34 -9
- package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +105 -23
- package/corpus/templates/analytics/server/lib/first-party-unbounded-panel-repair.ts +169 -0
- package/corpus/templates/analytics/server/plugins/auth.ts +1 -1
- package/corpus/templates/analytics/server/plugins/db.ts +41 -1
- package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +6 -3
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +0 -2
- package/corpus/templates/assets/app/routes/_index.tsx +1 -1
- package/corpus/templates/assets/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/assets/server/plugins/auth.ts +1 -1
- package/corpus/templates/brain/app/lib/brain.ts +0 -7
- package/corpus/templates/brain/app/routes/_index.tsx +1 -2
- package/corpus/templates/brain/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/brain/package.json +1 -1
- package/corpus/templates/brain/public/manifest.json +1 -1
- package/corpus/templates/brain/server/plugins/auth.ts +1 -1
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +0 -6
- package/corpus/templates/calendar/app/routes/_app._index.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/calendar/package.json +1 -1
- package/corpus/templates/calendar/public/manifest.json +1 -1
- package/corpus/templates/calendar/server/plugins/auth.ts +1 -1
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +0 -7
- package/corpus/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/clips/actions/import-loom-recording.ts +53 -98
- package/corpus/templates/clips/actions/lib/loom-import-job.ts +260 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +0 -7
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +1 -1
- package/corpus/templates/clips/app/routes/_index.tsx +1 -1
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +6 -0
- package/corpus/templates/clips/app/routes/record.tsx +5 -0
- package/corpus/templates/clips/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/clips/server/db/schema.ts +2 -0
- package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +2 -1
- package/corpus/templates/clips/server/plugins/auth.ts +1 -1
- package/corpus/templates/clips/server/plugins/db.ts +8 -0
- package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +56 -3
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -39
- package/corpus/templates/content/app/routes/_app._index.tsx +1 -2
- package/corpus/templates/content/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/content/package.json +1 -1
- package/corpus/templates/content/public/manifest.json +1 -1
- package/corpus/templates/content/server/plugins/auth.ts +1 -1
- package/corpus/templates/design/actions/edit-design.ts +66 -27
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +0 -2
- package/corpus/templates/design/app/routes/_index.tsx +1 -1
- package/corpus/templates/design/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/design/changelog/2026-07-25-design-edits-retry-concurrent-changes.md +6 -0
- package/corpus/templates/design/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/design/server/plugins/auth.ts +1 -1
- package/corpus/templates/design/server/source-workspace.ts +5 -1
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -10
- package/corpus/templates/dispatch/app/routes/overview.tsx +1 -1
- package/corpus/templates/dispatch/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/dispatch/package.json +1 -1
- package/corpus/templates/dispatch/public/manifest.json +1 -1
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +6 -4
- package/corpus/templates/forms/actions/create-form.ts +5 -1
- package/corpus/templates/forms/actions/export-responses.ts +22 -11
- package/corpus/templates/forms/actions/update-form.ts +5 -1
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +0 -35
- package/corpus/templates/forms/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-25-fields-can-be-created-without-ids.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-25-response-exports-use-file-storage.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/forms/package.json +1 -1
- package/corpus/templates/forms/public/manifest.json +1 -1
- package/corpus/templates/forms/server/lib/validate-fields.ts +43 -0
- package/corpus/templates/forms/server/plugins/auth.ts +1 -1
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +0 -2
- package/corpus/templates/macros/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/macros/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/macros/package.json +1 -1
- package/corpus/templates/macros/server/plugins/auth.ts +2 -2
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +0 -39
- package/corpus/templates/mail/app/routes/$view.$threadId.tsx +1 -1
- package/corpus/templates/mail/app/routes/$view.tsx +1 -1
- package/corpus/templates/mail/app/routes/_index.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/mail/package.json +1 -1
- package/corpus/templates/mail/public/manifest.json +1 -1
- package/corpus/templates/mail/server/plugins/auth.ts +1 -1
- package/corpus/templates/plan/agent-native.app-skill.json +1 -1
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +4 -9
- package/corpus/templates/plan/app/lib/app-config.ts +1 -1
- package/corpus/templates/plan/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/plan/package.json +1 -1
- package/corpus/templates/plan/public/manifest.json +1 -1
- package/corpus/templates/plan/server/plugins/auth.ts +1 -1
- package/corpus/templates/slides/actions/extract-pdf.ts +4 -4
- package/corpus/templates/slides/actions/import-file.ts +3 -4
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +0 -2
- package/corpus/templates/slides/app/routes/_index.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-25-gemini-image-models-updated.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-25-pdf-imports-tolerate-missing-canvas.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/corpus/templates/slides/package.json +1 -1
- package/corpus/templates/slides/public/manifest.json +1 -1
- package/corpus/templates/slides/server/handlers/image-providers/gemini.ts +5 -2
- package/corpus/templates/slides/server/lib/pdf-parse-setup.ts +150 -0
- package/corpus/templates/slides/server/plugins/auth.ts +1 -1
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +58 -4
- package/dist/a2a/client.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +63 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +49 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +9 -9
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
- package/dist/client/settings/SettingsTabsPage.js +7 -4
- package/dist/client/settings/SettingsTabsPage.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/extensions/url-safety.d.ts +9 -16
- package/dist/extensions/url-safety.d.ts.map +1 -1
- package/dist/extensions/url-safety.js +48 -4
- package/dist/extensions/url-safety.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 +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/builder-design-systems.js +1 -1
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +0 -7
- package/dist/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/dist/templates/default/app/root.tsx +1 -1
- package/dist/templates/default/public/manifest.json +2 -2
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +64 -1
- package/dist/vite/client.js.map +1 -1
- package/package.json +3 -3
- package/src/a2a/client.ts +57 -4
- package/src/agent/production-agent.ts +65 -1
- package/src/agent/run-store.ts +61 -1
- package/src/client/settings/SettingsPanel.tsx +9 -9
- package/src/client/settings/SettingsTabsPage.tsx +12 -2
- package/src/extensions/url-safety.ts +67 -4
- package/src/server/builder-design-systems.ts +1 -1
- package/src/templates/chat/app/components/layout/Sidebar.tsx +0 -7
- package/src/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
- package/src/templates/default/app/root.tsx +1 -1
- package/src/templates/default/public/manifest.json +2 -2
- package/src/vite/client.ts +64 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
runWithRequestContext,
|
|
3
5
|
verifyScopedAgentAccessToken,
|
|
4
6
|
} from "@agent-native/core/server";
|
|
5
|
-
import { eq } from "drizzle-orm";
|
|
7
|
+
import { and, eq, isNull, lt, or } from "drizzle-orm";
|
|
6
8
|
import {
|
|
7
9
|
defineEventHandler,
|
|
8
10
|
readBody,
|
|
@@ -14,6 +16,7 @@ import { z } from "zod";
|
|
|
14
16
|
import exportToBrain from "../../../../actions/export-to-brain.js";
|
|
15
17
|
import finalizeRecording from "../../../../actions/finalize-recording.js";
|
|
16
18
|
import { ensureRecordingSeekable } from "../../../../actions/lib/ensure-seekable-video.js";
|
|
19
|
+
import { runLoomImportJob } from "../../../../actions/lib/loom-import-job.js";
|
|
17
20
|
import requestTranscript from "../../../../actions/request-transcript.js";
|
|
18
21
|
import { getDb, schema } from "../../../db/index.js";
|
|
19
22
|
import {
|
|
@@ -24,13 +27,21 @@ import {
|
|
|
24
27
|
|
|
25
28
|
const bodySchema = z.object({
|
|
26
29
|
recordingId: z.string().min(1).max(200),
|
|
27
|
-
kind: z.enum([
|
|
30
|
+
kind: z.enum([
|
|
31
|
+
"media-ready",
|
|
32
|
+
"seekable",
|
|
33
|
+
"transcript",
|
|
34
|
+
"brain-export",
|
|
35
|
+
"loom-import",
|
|
36
|
+
]),
|
|
28
37
|
token: z.string().min(1),
|
|
29
38
|
delayMs: z.number().int().min(0).max(30_000).optional(),
|
|
30
39
|
retryAttempt: z.number().int().min(1).max(10).optional(),
|
|
31
40
|
regenerate: z.boolean().optional(),
|
|
32
41
|
});
|
|
33
42
|
|
|
43
|
+
const LOOM_IMPORT_LEASE_MS = 30 * 60 * 1000;
|
|
44
|
+
|
|
34
45
|
export default defineEventHandler(async (event: H3Event) => {
|
|
35
46
|
const parsed = bodySchema.safeParse(await readBody(event).catch(() => null));
|
|
36
47
|
if (!parsed.success) {
|
|
@@ -63,7 +74,8 @@ export default defineEventHandler(async (event: H3Event) => {
|
|
|
63
74
|
setResponseStatus(event, 404);
|
|
64
75
|
return { ok: false, error: "Recording not found" };
|
|
65
76
|
}
|
|
66
|
-
const requiredStatus =
|
|
77
|
+
const requiredStatus =
|
|
78
|
+
kind === "media-ready" || kind === "loom-import" ? "processing" : "ready";
|
|
67
79
|
if (recording.status !== requiredStatus) {
|
|
68
80
|
return {
|
|
69
81
|
ok: true,
|
|
@@ -112,6 +124,47 @@ export default defineEventHandler(async (event: H3Event) => {
|
|
|
112
124
|
return { ok: true, kind, result };
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
if (kind === "loom-import") {
|
|
128
|
+
const claimId = randomUUID();
|
|
129
|
+
const claimStartedAt = new Date().toISOString();
|
|
130
|
+
const claimExpiredBefore = new Date(
|
|
131
|
+
Date.now() - LOOM_IMPORT_LEASE_MS,
|
|
132
|
+
).toISOString();
|
|
133
|
+
const [claimed] = await getDb()
|
|
134
|
+
.update(schema.recordings)
|
|
135
|
+
.set({
|
|
136
|
+
loomImportClaimId: claimId,
|
|
137
|
+
loomImportClaimedAt: claimStartedAt,
|
|
138
|
+
})
|
|
139
|
+
.where(
|
|
140
|
+
and(
|
|
141
|
+
eq(schema.recordings.id, recordingId),
|
|
142
|
+
eq(schema.recordings.status, "processing"),
|
|
143
|
+
or(
|
|
144
|
+
isNull(schema.recordings.loomImportClaimId),
|
|
145
|
+
isNull(schema.recordings.loomImportClaimedAt),
|
|
146
|
+
lt(schema.recordings.loomImportClaimedAt, claimExpiredBefore),
|
|
147
|
+
),
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
.returning({ id: schema.recordings.id });
|
|
151
|
+
if (!claimed) {
|
|
152
|
+
return {
|
|
153
|
+
ok: true,
|
|
154
|
+
recordingId,
|
|
155
|
+
kind,
|
|
156
|
+
skipped: true,
|
|
157
|
+
reason: "loom-import-already-running",
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const result = await runLoomImportJob({
|
|
161
|
+
recordingId,
|
|
162
|
+
ownerEmail: recording.ownerEmail,
|
|
163
|
+
claimId,
|
|
164
|
+
});
|
|
165
|
+
return { ok: true, kind, result };
|
|
166
|
+
}
|
|
167
|
+
|
|
115
168
|
if (kind === "brain-export") {
|
|
116
169
|
const result = await exportToBrain.run({
|
|
117
170
|
recordingId,
|
|
@@ -34,7 +34,6 @@ import type {
|
|
|
34
34
|
} from "@shared/api";
|
|
35
35
|
import { CONTENT_DATABASE_PERSONAL_VIEW_OVERRIDES_VERSION } from "@shared/api";
|
|
36
36
|
import {
|
|
37
|
-
IconHierarchy2,
|
|
38
37
|
IconFolder,
|
|
39
38
|
IconFolderOpen,
|
|
40
39
|
IconPlus,
|
|
@@ -637,7 +636,6 @@ export function DocumentSidebar({
|
|
|
637
636
|
}, [setStoredCollapsedSections]);
|
|
638
637
|
const [removeLocalFilesDialogOpen, setRemoveLocalFilesDialogOpen] =
|
|
639
638
|
useState(false);
|
|
640
|
-
const agentActive = location.pathname.startsWith("/agent");
|
|
641
639
|
const settingsActive = location.pathname.startsWith("/settings");
|
|
642
640
|
const sensors = useSensors(
|
|
643
641
|
useSensor(PointerSensor, {
|
|
@@ -1326,23 +1324,6 @@ export function DocumentSidebar({
|
|
|
1326
1324
|
/>
|
|
1327
1325
|
);
|
|
1328
1326
|
|
|
1329
|
-
const renderAgentNavButton = () => (
|
|
1330
|
-
<Link
|
|
1331
|
-
to="/agent"
|
|
1332
|
-
className={cn(
|
|
1333
|
-
"flex h-8 w-full items-center gap-2 rounded-md px-2 text-sm",
|
|
1334
|
-
agentActive
|
|
1335
|
-
? "bg-accent text-accent-foreground"
|
|
1336
|
-
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
|
|
1337
|
-
)}
|
|
1338
|
-
>
|
|
1339
|
-
<IconHierarchy2 size={15} className="shrink-0" />
|
|
1340
|
-
<span className="min-w-0 flex-1 truncate text-start">
|
|
1341
|
-
{t("settings.agentTitle")}
|
|
1342
|
-
</span>
|
|
1343
|
-
</Link>
|
|
1344
|
-
);
|
|
1345
|
-
|
|
1346
1327
|
const toggleSection = (id: SidebarSectionId) => {
|
|
1347
1328
|
setStoredCollapsedSections((current) => {
|
|
1348
1329
|
const normalized = normalizeCollapsedSections(current);
|
|
@@ -1881,22 +1862,6 @@ export function DocumentSidebar({
|
|
|
1881
1862
|
return (
|
|
1882
1863
|
<div className="agent-layout-left-drawer flex h-full w-12 flex-col items-center gap-1 border-e border-border bg-sidebar py-3 transition-[width] duration-200 ease-out">
|
|
1883
1864
|
{renderCollapsedNewButton()}
|
|
1884
|
-
<Tooltip>
|
|
1885
|
-
<TooltipTrigger asChild>
|
|
1886
|
-
<Link
|
|
1887
|
-
to="/agent"
|
|
1888
|
-
className={cn(
|
|
1889
|
-
"w-10 h-10 flex items-center justify-center rounded-lg hover:bg-accent",
|
|
1890
|
-
agentActive
|
|
1891
|
-
? "bg-accent text-accent-foreground"
|
|
1892
|
-
: "text-muted-foreground hover:text-foreground",
|
|
1893
|
-
)}
|
|
1894
|
-
>
|
|
1895
|
-
<IconHierarchy2 size={16} />
|
|
1896
|
-
</Link>
|
|
1897
|
-
</TooltipTrigger>
|
|
1898
|
-
<TooltipContent>{t("settings.agentTitle")}</TooltipContent>
|
|
1899
|
-
</Tooltip>
|
|
1900
1865
|
<Tooltip>
|
|
1901
1866
|
<TooltipTrigger asChild>
|
|
1902
1867
|
<Link
|
|
@@ -2091,10 +2056,7 @@ export function DocumentSidebar({
|
|
|
2091
2056
|
</ScrollArea>
|
|
2092
2057
|
|
|
2093
2058
|
<div className="shrink-0 px-3 py-2">
|
|
2094
|
-
<div className="space-y-1">
|
|
2095
|
-
{renderAgentNavButton()}
|
|
2096
|
-
{renderSettingsNavButton()}
|
|
2097
|
-
</div>
|
|
2059
|
+
<div className="space-y-1">{renderSettingsNavButton()}</div>
|
|
2098
2060
|
</div>
|
|
2099
2061
|
|
|
2100
2062
|
<div className="shrink-0">
|
|
@@ -7,8 +7,7 @@ import { QueryErrorState } from "@/components/QueryErrorState";
|
|
|
7
7
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
8
8
|
import { useDocuments } from "@/hooks/use-documents";
|
|
9
9
|
|
|
10
|
-
const SEO_TITLE =
|
|
11
|
-
"Agent-Native Content - Open Source, agent-friendly Obsidian alternative";
|
|
10
|
+
const SEO_TITLE = "Content - Open Source, agent-friendly Obsidian alternative";
|
|
12
11
|
const SEO_DESCRIPTION =
|
|
13
12
|
"Open Source MDX editor for local docs, knowledge bases, and content systems, with custom blocks and agent-assisted editing.";
|
|
14
13
|
|
|
@@ -2,7 +2,7 @@ import { createAuthPlugin } from "@agent-native/core/server";
|
|
|
2
2
|
|
|
3
3
|
export default createAuthPlugin({
|
|
4
4
|
marketing: {
|
|
5
|
-
appName: "
|
|
5
|
+
appName: "Content",
|
|
6
6
|
tagline:
|
|
7
7
|
"Open-source Obsidian for MDX: your AI agent edits local docs, creates custom blocks, and organizes everything alongside you.",
|
|
8
8
|
features: [
|
|
@@ -18,6 +18,7 @@ import { z } from "zod";
|
|
|
18
18
|
import { getDb, schema } from "../server/db/index.js";
|
|
19
19
|
import {
|
|
20
20
|
readLiveSourceFile,
|
|
21
|
+
SourceWorkspaceEditConflictError,
|
|
21
22
|
writeInlineSourceFile,
|
|
22
23
|
type SourceWorkspaceFile,
|
|
23
24
|
} from "../server/source-workspace.js";
|
|
@@ -303,36 +304,28 @@ export default defineAction({
|
|
|
303
304
|
);
|
|
304
305
|
}
|
|
305
306
|
|
|
306
|
-
// Read the LIVE base (collab text when present, else the SQL row) right
|
|
307
|
-
// before transforming, and carry its versionHash through to the write
|
|
308
|
-
// below. writeInlineSourceFile re-reads the live text immediately before
|
|
309
|
-
// its own applyText/DB write and rejects if it no longer matches this
|
|
310
|
-
// hash — closing the race window where a concurrent editor/agent write
|
|
311
|
-
// lands between this read and the persist (the same stale-diff-base bug
|
|
312
|
-
// fixed for insert-design-native-asset.ts and insert-asset.ts: a diff
|
|
313
|
-
// computed from a stale base, char-diffed into a collab doc that has
|
|
314
|
-
// since moved on, corrupts or drops the other writer's change).
|
|
315
|
-
const workspaceFile: SourceWorkspaceFile = {
|
|
316
|
-
id: file.id,
|
|
317
|
-
designId: file.designId,
|
|
318
|
-
filename: file.filename ?? "",
|
|
319
|
-
fileType: file.fileType ?? "html",
|
|
320
|
-
content: file.content,
|
|
321
|
-
createdAt: null,
|
|
322
|
-
updatedAt: null,
|
|
323
|
-
};
|
|
324
|
-
const live = await readLiveSourceFile(workspaceFile);
|
|
325
|
-
const base = live.content;
|
|
326
|
-
|
|
327
307
|
const resolvedMode =
|
|
328
308
|
mode ??
|
|
329
309
|
(replacementContent !== undefined ? "replace-file" : "search-replace");
|
|
330
|
-
const { content: nextContent, applied } =
|
|
331
|
-
resolvedMode === "replace-file"
|
|
332
|
-
? { content: replacementContent ?? "", applied: 0 }
|
|
333
|
-
: applySearchReplaceEdits(base, edits ?? []);
|
|
334
|
-
const changed = nextContent !== base;
|
|
335
310
|
|
|
311
|
+
// A concurrent human/agent write can land between reading the live file
|
|
312
|
+
// and persisting this edit (writeInlineSourceFile throws
|
|
313
|
+
// SourceWorkspaceEditConflictError when that happens — see its own
|
|
314
|
+
// comment for the CAS/collab details). search-replace edits are anchored
|
|
315
|
+
// to specific text rather than a stale full snapshot, so on conflict we
|
|
316
|
+
// can just re-read the fresh content and reapply the SAME edits against
|
|
317
|
+
// it instead of forcing the agent to make a separate get-design-snapshot
|
|
318
|
+
// round trip and guess again. replace-file sends a full document computed
|
|
319
|
+
// from a point-in-time snapshot — retrying that blind could silently
|
|
320
|
+
// clobber whatever the concurrent writer did, so it still fails closed on
|
|
321
|
+
// the first conflict.
|
|
322
|
+
const MAX_EDIT_CONFLICT_RETRIES = 2;
|
|
323
|
+
|
|
324
|
+
let live: Awaited<ReturnType<typeof readLiveSourceFile>>;
|
|
325
|
+
let base = "";
|
|
326
|
+
let nextContent = "";
|
|
327
|
+
let applied = 0;
|
|
328
|
+
let changed = false;
|
|
336
329
|
let creativeContext:
|
|
337
330
|
| {
|
|
338
331
|
contextMode: "off" | "auto" | "pinned";
|
|
@@ -352,7 +345,43 @@ export default defineAction({
|
|
|
352
345
|
}
|
|
353
346
|
| undefined;
|
|
354
347
|
|
|
355
|
-
|
|
348
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
349
|
+
// Refetch the SQL content on retries — readLiveSourceFile only falls
|
|
350
|
+
// back to this when no collab doc exists yet, so a conflict caused by
|
|
351
|
+
// a plain SQL writer (no live collab session) needs a fresh row here
|
|
352
|
+
// or every retry would recompute the exact same stale versionHash.
|
|
353
|
+
const currentContent =
|
|
354
|
+
attempt === 0
|
|
355
|
+
? file.content
|
|
356
|
+
: (
|
|
357
|
+
await db
|
|
358
|
+
.select({ content: schema.designFiles.content })
|
|
359
|
+
.from(schema.designFiles)
|
|
360
|
+
.where(eq(schema.designFiles.id, file.id))
|
|
361
|
+
.limit(1)
|
|
362
|
+
)[0]?.content;
|
|
363
|
+
|
|
364
|
+
const workspaceFile: SourceWorkspaceFile = {
|
|
365
|
+
id: file.id,
|
|
366
|
+
designId: file.designId,
|
|
367
|
+
filename: file.filename ?? "",
|
|
368
|
+
fileType: file.fileType ?? "html",
|
|
369
|
+
content: currentContent,
|
|
370
|
+
createdAt: null,
|
|
371
|
+
updatedAt: null,
|
|
372
|
+
};
|
|
373
|
+
live = await readLiveSourceFile(workspaceFile);
|
|
374
|
+
base = live.content;
|
|
375
|
+
|
|
376
|
+
({ content: nextContent, applied } =
|
|
377
|
+
resolvedMode === "replace-file"
|
|
378
|
+
? { content: replacementContent ?? "", applied: 0 }
|
|
379
|
+
: applySearchReplaceEdits(base, edits ?? []));
|
|
380
|
+
changed = nextContent !== base;
|
|
381
|
+
creativeContext = undefined;
|
|
382
|
+
|
|
383
|
+
if (!changed) break;
|
|
384
|
+
|
|
356
385
|
const previous =
|
|
357
386
|
contextModeOverride === "off"
|
|
358
387
|
? null
|
|
@@ -439,6 +468,15 @@ export default defineAction({
|
|
|
439
468
|
content: nextContent,
|
|
440
469
|
expectedVersionHash: live.versionHash,
|
|
441
470
|
});
|
|
471
|
+
} catch (error) {
|
|
472
|
+
if (
|
|
473
|
+
error instanceof SourceWorkspaceEditConflictError &&
|
|
474
|
+
resolvedMode === "search-replace" &&
|
|
475
|
+
attempt < MAX_EDIT_CONFLICT_RETRIES
|
|
476
|
+
) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
throw error;
|
|
442
480
|
} finally {
|
|
443
481
|
agentLeaveDocument(file.id);
|
|
444
482
|
}
|
|
@@ -448,6 +486,7 @@ export default defineAction({
|
|
|
448
486
|
artifactId: designId,
|
|
449
487
|
...creativeContext,
|
|
450
488
|
});
|
|
489
|
+
break;
|
|
451
490
|
}
|
|
452
491
|
|
|
453
492
|
return {
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
IconPencil,
|
|
10
10
|
IconTemplate,
|
|
11
11
|
IconPalette,
|
|
12
|
-
IconHierarchy2,
|
|
13
12
|
IconSettings,
|
|
14
13
|
IconLayoutSidebarLeftCollapse,
|
|
15
14
|
IconLayoutSidebarLeftExpand,
|
|
@@ -37,7 +36,6 @@ const navItems = [
|
|
|
37
36
|
];
|
|
38
37
|
|
|
39
38
|
const bottomNavItems = [
|
|
40
|
-
{ icon: IconHierarchy2, labelKey: "settings.agentTitle", href: "/agent" },
|
|
41
39
|
{ icon: IconSettings, labelKey: "navigation.settings", href: "/settings" },
|
|
42
40
|
];
|
|
43
41
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { default } from "../pages/Index";
|
|
2
2
|
|
|
3
3
|
const SEO_TITLE =
|
|
4
|
-
"
|
|
4
|
+
"Design - Open Source AI design tool for agent-built prototypes";
|
|
5
5
|
const SEO_DESCRIPTION =
|
|
6
6
|
"Open Source AI design tool for creating, remixing, and sharing responsive prototypes, design systems, and coding handoffs.";
|
|
7
7
|
|
|
@@ -7,7 +7,7 @@ export default createAuthPlugin({
|
|
|
7
7
|
// still go through authenticated actions.
|
|
8
8
|
workspaceAppPublicPaths: ["/visual-edit", "/design", "/present"],
|
|
9
9
|
marketing: {
|
|
10
|
-
appName: "
|
|
10
|
+
appName: "Design",
|
|
11
11
|
tagline:
|
|
12
12
|
"Design and prototype by describing what you want. The AI agent turns your ideas into interactive, fully responsive designs in seconds.",
|
|
13
13
|
features: [
|
|
@@ -331,7 +331,11 @@ export async function writeInlineSourceFile(args: {
|
|
|
331
331
|
args.expectedVersionHash &&
|
|
332
332
|
args.expectedVersionHash !== current.versionHash
|
|
333
333
|
) {
|
|
334
|
-
|
|
334
|
+
// Typed so callers can catch-and-retry the same way they already do for
|
|
335
|
+
// the other two conflict sites below (see apply-shader-fill.ts /
|
|
336
|
+
// apply-component-prop-edit.ts / edit-design.ts) instead of only
|
|
337
|
+
// matching on message text.
|
|
338
|
+
throw new SourceWorkspaceEditConflictError(
|
|
335
339
|
"Source file changed since it was read. Re-read the file and retry.",
|
|
336
340
|
);
|
|
337
341
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { DispatchExtensionConfig } from "@agent-native/dispatch/components";
|
|
2
|
-
import { IconHierarchy2 } from "@tabler/icons-react";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Local Dispatch extensions for this generated workspace.
|
|
@@ -26,14 +25,6 @@ import { IconHierarchy2 } from "@tabler/icons-react";
|
|
|
26
25
|
* } satisfies DispatchExtensionConfig;
|
|
27
26
|
*/
|
|
28
27
|
export const dispatchExtensions = {
|
|
29
|
-
navItems: [
|
|
30
|
-
{
|
|
31
|
-
id: "agent",
|
|
32
|
-
to: "/agent",
|
|
33
|
-
label: "Manage agent",
|
|
34
|
-
icon: IconHierarchy2,
|
|
35
|
-
section: "operations",
|
|
36
|
-
},
|
|
37
|
-
],
|
|
28
|
+
navItems: [],
|
|
38
29
|
queryKeys: [],
|
|
39
30
|
} satisfies DispatchExtensionConfig;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import type { LoaderFunctionArgs } from "react-router";
|
|
7
7
|
|
|
8
8
|
const SEO_TITLE =
|
|
9
|
-
"
|
|
9
|
+
"Dispatch - Open Source workspace control plane for AI agents";
|
|
10
10
|
const SEO_DESCRIPTION =
|
|
11
11
|
"Open Source workspace control plane for AI agents to manage apps, secrets, approvals, messages, jobs, and cross-app delegation.";
|
|
12
12
|
|
|
@@ -48,14 +48,16 @@ status, visibility, and an "Open editor" action.
|
|
|
48
48
|
|
|
49
49
|
## Exporting Responses
|
|
50
50
|
|
|
51
|
-
Use `export-responses` to export to CSV or JSON
|
|
51
|
+
Use `export-responses` to export to CSV or JSON. The export is uploaded to
|
|
52
|
+
configured file storage (never written to local disk — serverless hosts have
|
|
53
|
+
a read-only filesystem) and the action returns the resulting file URL:
|
|
52
54
|
|
|
53
55
|
```bash
|
|
54
56
|
# CSV export (default)
|
|
55
|
-
pnpm action export-responses --form <form-id>
|
|
57
|
+
pnpm action export-responses --form <form-id>
|
|
56
58
|
|
|
57
59
|
# JSON export
|
|
58
|
-
pnpm action export-responses --form <form-id> --
|
|
60
|
+
pnpm action export-responses --form <form-id> --format json
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
The CSV includes headers derived from field labels. Array values (multiselect) are joined with semicolons.
|
|
@@ -113,7 +115,7 @@ To analyze responses, the workflow is:
|
|
|
113
115
|
| ---------------------- | ---------------------------------------------------------------------------- |
|
|
114
116
|
| "@Form setup?" | `preview-form --formId <id>` and answer from the returned fields/settings |
|
|
115
117
|
| "How many responses?" | `response-insights --formId <id>` and report `summary.responses` |
|
|
116
|
-
| "Export to CSV" | `export-responses --form <id
|
|
118
|
+
| "Export to CSV" | `export-responses --form <id>` |
|
|
117
119
|
| "Submissions by day" | `response-insights --formId <id> --days 30` |
|
|
118
120
|
| "Summarize feedback" | `response-insights`, then `list-responses` if more detail is needed |
|
|
119
121
|
| "Average rating" | `list-responses`, compute from rating fields and state the sampled row count |
|
|
@@ -9,7 +9,10 @@ import { z } from "zod";
|
|
|
9
9
|
|
|
10
10
|
import { getDb, schema } from "../server/db/index.js";
|
|
11
11
|
import { assertIntegrationUrlsAllowed } from "../server/lib/integrations.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
assertValidFields,
|
|
14
|
+
normalizeFieldIds,
|
|
15
|
+
} from "../server/lib/validate-fields.js";
|
|
13
16
|
import type { FormField, FormSettings } from "../shared/types.js";
|
|
14
17
|
import { assertPublishableForm } from "./lib/assert-publishable-form.js";
|
|
15
18
|
|
|
@@ -88,6 +91,7 @@ export default defineAction({
|
|
|
88
91
|
fields = args.fields as unknown as FormField[];
|
|
89
92
|
}
|
|
90
93
|
}
|
|
94
|
+
fields = normalizeFieldIds(fields) as FormField[];
|
|
91
95
|
assertValidFields(fields);
|
|
92
96
|
|
|
93
97
|
const defaultSettings: FormSettings = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
|
|
3
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { uploadFile } from "@agent-native/core/file-upload";
|
|
3
|
+
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
4
4
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
5
5
|
import { eq, desc } from "drizzle-orm";
|
|
6
6
|
import { z } from "zod";
|
|
@@ -12,7 +12,6 @@ export default defineAction({
|
|
|
12
12
|
description: "Export form responses to CSV or JSON file.",
|
|
13
13
|
schema: z.object({
|
|
14
14
|
form: z.string().describe("Form ID (required)"),
|
|
15
|
-
output: z.string().optional().describe("Output file path"),
|
|
16
15
|
format: z.enum(["csv", "json"]).optional().describe("Export format"),
|
|
17
16
|
}),
|
|
18
17
|
http: false,
|
|
@@ -28,10 +27,10 @@ export default defineAction({
|
|
|
28
27
|
.orderBy(desc(schema.responses.submittedAt));
|
|
29
28
|
|
|
30
29
|
const fields = JSON.parse(form.fields);
|
|
31
|
-
const fmt =
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
30
|
+
const fmt = args.format || "csv";
|
|
31
|
+
const filename = `export-${formId}.${fmt === "json" ? "json" : "csv"}`;
|
|
32
|
+
const ownerEmail = getRequestUserEmail() ?? undefined;
|
|
33
|
+
let fileBody: string;
|
|
35
34
|
|
|
36
35
|
if (fmt === "json") {
|
|
37
36
|
const data = responses.map((r) => ({
|
|
@@ -42,7 +41,7 @@ export default defineAction({
|
|
|
42
41
|
clientSurface: r.clientSurface ?? null,
|
|
43
42
|
...JSON.parse(r.data),
|
|
44
43
|
}));
|
|
45
|
-
|
|
44
|
+
fileBody = JSON.stringify(data, null, 2);
|
|
46
45
|
} else {
|
|
47
46
|
const headers = [
|
|
48
47
|
"ID",
|
|
@@ -74,17 +73,29 @@ export default defineAction({
|
|
|
74
73
|
// with a single quote so spreadsheets treat it as literal text.
|
|
75
74
|
const neutralize = (cell: string) =>
|
|
76
75
|
/^[=+\-@\t\r]/.test(cell) ? `'${cell}` : cell;
|
|
77
|
-
|
|
76
|
+
fileBody = [headers, ...rows]
|
|
78
77
|
.map((row) =>
|
|
79
78
|
row
|
|
80
79
|
.map((cell: string) => `"${neutralize(cell).replace(/"/g, '""')}"`)
|
|
81
80
|
.join(","),
|
|
82
81
|
)
|
|
83
82
|
.join("\n");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const uploaded = await uploadFile({
|
|
86
|
+
data: Buffer.from(fileBody, "utf8"),
|
|
87
|
+
mimeType: fmt === "json" ? "application/json" : "text/csv",
|
|
88
|
+
filename,
|
|
89
|
+
ownerEmail,
|
|
90
|
+
}).catch(() => null);
|
|
84
91
|
|
|
85
|
-
|
|
92
|
+
if (!uploaded) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
"Export was generated but not saved because file storage is not configured. " +
|
|
95
|
+
"Connect or reconnect Builder.io in Settings → File uploads, or register a custom provider.",
|
|
96
|
+
);
|
|
86
97
|
}
|
|
87
98
|
|
|
88
|
-
return `Exported ${responses.length} responses to ${
|
|
99
|
+
return `Exported ${responses.length} responses to ${uploaded.url}`;
|
|
89
100
|
},
|
|
90
101
|
});
|
|
@@ -6,7 +6,10 @@ import { z } from "zod";
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
7
|
import { assertIntegrationUrlsAllowed } from "../server/lib/integrations.js";
|
|
8
8
|
import { invalidatePublicFormCache } from "../server/lib/public-form-ssr.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
assertValidFields,
|
|
11
|
+
normalizeFieldIds,
|
|
12
|
+
} from "../server/lib/validate-fields.js";
|
|
10
13
|
import type { FormField, FormSettings } from "../shared/types.js";
|
|
11
14
|
import { assertPublishableForm } from "./lib/assert-publishable-form.js";
|
|
12
15
|
|
|
@@ -81,6 +84,7 @@ export default defineAction({
|
|
|
81
84
|
} else {
|
|
82
85
|
parsedFields = args.fields;
|
|
83
86
|
}
|
|
87
|
+
parsedFields = normalizeFieldIds(parsedFields);
|
|
84
88
|
assertValidFields(parsedFields);
|
|
85
89
|
updates.fields = JSON.stringify(parsedFields);
|
|
86
90
|
}
|