stagent 0.6.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +21 -2
  2. package/dist/cli.js +226 -1
  3. package/docs/.coverage-gaps.json +66 -16
  4. package/docs/.last-generated +1 -1
  5. package/docs/features/dashboard-kanban.md +13 -7
  6. package/docs/features/settings.md +15 -3
  7. package/docs/features/tables.md +122 -0
  8. package/docs/index.md +3 -2
  9. package/docs/journeys/developer.md +26 -16
  10. package/docs/journeys/personal-use.md +23 -9
  11. package/docs/journeys/power-user.md +40 -14
  12. package/docs/journeys/work-use.md +43 -15
  13. package/docs/manifest.json +27 -17
  14. package/package.json +3 -1
  15. package/src/app/api/chat/entities/search/route.ts +12 -3
  16. package/src/app/api/projects/[id]/route.ts +37 -0
  17. package/src/app/api/projects/__tests__/delete-project.test.ts +12 -0
  18. package/src/app/api/snapshots/[id]/restore/route.ts +62 -0
  19. package/src/app/api/snapshots/[id]/route.ts +44 -0
  20. package/src/app/api/snapshots/route.ts +54 -0
  21. package/src/app/api/snapshots/settings/route.ts +67 -0
  22. package/src/app/api/tables/[id]/charts/[chartId]/route.ts +89 -0
  23. package/src/app/api/tables/[id]/charts/route.ts +72 -0
  24. package/src/app/api/tables/[id]/columns/route.ts +70 -0
  25. package/src/app/api/tables/[id]/export/route.ts +94 -0
  26. package/src/app/api/tables/[id]/history/route.ts +15 -0
  27. package/src/app/api/tables/[id]/import/route.ts +111 -0
  28. package/src/app/api/tables/[id]/route.ts +86 -0
  29. package/src/app/api/tables/[id]/rows/[rowId]/history/route.ts +32 -0
  30. package/src/app/api/tables/[id]/rows/[rowId]/route.ts +51 -0
  31. package/src/app/api/tables/[id]/rows/route.ts +101 -0
  32. package/src/app/api/tables/[id]/triggers/[triggerId]/route.ts +65 -0
  33. package/src/app/api/tables/[id]/triggers/route.ts +122 -0
  34. package/src/app/api/tables/route.ts +65 -0
  35. package/src/app/api/tables/templates/route.ts +92 -0
  36. package/src/app/settings/page.tsx +2 -0
  37. package/src/app/tables/[id]/page.tsx +67 -0
  38. package/src/app/tables/page.tsx +21 -0
  39. package/src/app/tables/templates/page.tsx +19 -0
  40. package/src/components/chat/chat-table-result.tsx +139 -0
  41. package/src/components/documents/document-browser.tsx +1 -1
  42. package/src/components/projects/project-form-sheet.tsx +3 -27
  43. package/src/components/schedules/schedule-form.tsx +5 -27
  44. package/src/components/settings/data-management-section.tsx +17 -12
  45. package/src/components/settings/database-snapshots-section.tsx +469 -0
  46. package/src/components/shared/app-sidebar.tsx +2 -0
  47. package/src/components/shared/document-picker-sheet.tsx +214 -11
  48. package/src/components/tables/table-browser.tsx +234 -0
  49. package/src/components/tables/table-cell-editor.tsx +226 -0
  50. package/src/components/tables/table-chart-builder.tsx +288 -0
  51. package/src/components/tables/table-chart-view.tsx +146 -0
  52. package/src/components/tables/table-column-header.tsx +103 -0
  53. package/src/components/tables/table-column-sheet.tsx +331 -0
  54. package/src/components/tables/table-create-sheet.tsx +240 -0
  55. package/src/components/tables/table-detail-sheet.tsx +144 -0
  56. package/src/components/tables/table-detail-tabs.tsx +278 -0
  57. package/src/components/tables/table-grid.tsx +61 -0
  58. package/src/components/tables/table-history-tab.tsx +148 -0
  59. package/src/components/tables/table-import-wizard.tsx +542 -0
  60. package/src/components/tables/table-list-table.tsx +95 -0
  61. package/src/components/tables/table-relation-combobox.tsx +217 -0
  62. package/src/components/tables/table-spreadsheet.tsx +499 -0
  63. package/src/components/tables/table-template-gallery.tsx +162 -0
  64. package/src/components/tables/table-template-preview.tsx +219 -0
  65. package/src/components/tables/table-toolbar.tsx +79 -0
  66. package/src/components/tables/table-triggers-tab.tsx +446 -0
  67. package/src/components/tables/types.ts +6 -0
  68. package/src/components/tables/use-spreadsheet-keys.ts +171 -0
  69. package/src/components/tables/utils.ts +29 -0
  70. package/src/components/tasks/task-create-panel.tsx +5 -31
  71. package/src/components/tasks/task-edit-dialog.tsx +5 -27
  72. package/src/components/workflows/workflow-form-view.tsx +5 -29
  73. package/src/components/workflows/workflow-status-view.tsx +1 -1
  74. package/src/instrumentation.ts +3 -0
  75. package/src/lib/agents/__tests__/claude-agent.test.ts +5 -1
  76. package/src/lib/agents/claude-agent.ts +3 -1
  77. package/src/lib/agents/runtime/anthropic-direct.ts +29 -0
  78. package/src/lib/agents/runtime/openai-direct.ts +29 -0
  79. package/src/lib/chat/stagent-tools.ts +2 -0
  80. package/src/lib/chat/tool-catalog.ts +34 -0
  81. package/src/lib/chat/tools/table-tools.ts +955 -0
  82. package/src/lib/constants/table-status.ts +68 -0
  83. package/src/lib/data/__tests__/clear.test.ts +1 -1
  84. package/src/lib/data/clear.ts +45 -0
  85. package/src/lib/data/seed-data/__tests__/profiles.test.ts +28 -23
  86. package/src/lib/data/seed-data/conversations.ts +350 -42
  87. package/src/lib/data/seed-data/documents.ts +564 -591
  88. package/src/lib/data/seed-data/learned-context.ts +101 -22
  89. package/src/lib/data/seed-data/notifications.ts +344 -70
  90. package/src/lib/data/seed-data/profile-test-results.ts +92 -11
  91. package/src/lib/data/seed-data/profiles.ts +144 -46
  92. package/src/lib/data/seed-data/projects.ts +50 -18
  93. package/src/lib/data/seed-data/repo-imports.ts +28 -13
  94. package/src/lib/data/seed-data/schedules.ts +208 -41
  95. package/src/lib/data/seed-data/table-templates.ts +234 -0
  96. package/src/lib/data/seed-data/tasks.ts +614 -116
  97. package/src/lib/data/seed-data/usage-ledger.ts +182 -103
  98. package/src/lib/data/seed-data/user-tables.ts +203 -0
  99. package/src/lib/data/seed-data/views.ts +52 -7
  100. package/src/lib/data/seed-data/workflows.ts +231 -84
  101. package/src/lib/data/seed.ts +55 -14
  102. package/src/lib/data/tables.ts +417 -0
  103. package/src/lib/db/bootstrap.ts +227 -0
  104. package/src/lib/db/index.ts +9 -0
  105. package/src/lib/db/migrations/0019_add_tables_feature.sql +160 -0
  106. package/src/lib/db/migrations/0020_add_table_triggers.sql +19 -0
  107. package/src/lib/db/migrations/0021_add_row_history.sql +15 -0
  108. package/src/lib/db/schema.ts +368 -0
  109. package/src/lib/snapshots/auto-backup.ts +132 -0
  110. package/src/lib/snapshots/retention.ts +64 -0
  111. package/src/lib/snapshots/snapshot-manager.ts +429 -0
  112. package/src/lib/tables/computed.ts +61 -0
  113. package/src/lib/tables/context-builder.ts +139 -0
  114. package/src/lib/tables/formula-engine.ts +415 -0
  115. package/src/lib/tables/history.ts +115 -0
  116. package/src/lib/tables/import.ts +343 -0
  117. package/src/lib/tables/query-builder.ts +152 -0
  118. package/src/lib/tables/trigger-evaluator.ts +146 -0
  119. package/src/lib/tables/types.ts +141 -0
  120. package/src/lib/tables/validation.ts +119 -0
  121. package/src/lib/utils/stagent-paths.ts +20 -0
  122. package/tsconfig.json +3 -1
  123. /package/docs/features/{playbook.md → user-guide.md} +0 -0
@@ -0,0 +1,92 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { randomUUID } from "crypto";
3
+ import { db } from "@/lib/db";
4
+ import { userTableTemplates } from "@/lib/db/schema";
5
+ import { listTemplates, getTable, listRows } from "@/lib/data/tables";
6
+ import { listTemplatesSchema } from "@/lib/tables/validation";
7
+
8
+ export async function GET(req: NextRequest) {
9
+ try {
10
+ const url = new URL(req.url);
11
+ const category = url.searchParams.get("category") ?? undefined;
12
+ const scope = url.searchParams.get("scope") ?? undefined;
13
+
14
+ const parsed = listTemplatesSchema.safeParse({ category, scope });
15
+ if (!parsed.success) {
16
+ return NextResponse.json(
17
+ { error: parsed.error.flatten() },
18
+ { status: 400 }
19
+ );
20
+ }
21
+
22
+ const templates = await listTemplates(parsed.data);
23
+ return NextResponse.json(templates);
24
+ } catch (err) {
25
+ console.error("[tables] GET templates error:", err);
26
+ return NextResponse.json(
27
+ { error: "Failed to list templates" },
28
+ { status: 500 }
29
+ );
30
+ }
31
+ }
32
+
33
+ /**
34
+ * POST /api/tables/templates — Save a table as a user-scoped template.
35
+ */
36
+ export async function POST(req: NextRequest) {
37
+ try {
38
+ const body = await req.json();
39
+ const { tableId, name, description, category, includeSampleData } = body as {
40
+ tableId?: string;
41
+ name?: string;
42
+ description?: string;
43
+ category?: string;
44
+ includeSampleData?: boolean;
45
+ };
46
+
47
+ if (!tableId || !name) {
48
+ return NextResponse.json(
49
+ { error: "tableId and name are required" },
50
+ { status: 400 }
51
+ );
52
+ }
53
+
54
+ const table = await getTable(tableId);
55
+ if (!table) {
56
+ return NextResponse.json({ error: "Table not found" }, { status: 404 });
57
+ }
58
+
59
+ // Optionally snapshot first 5 rows as sample data
60
+ let sampleData: string | null = null;
61
+ if (includeSampleData) {
62
+ const rows = await listRows(tableId, { limit: 5 });
63
+ sampleData = JSON.stringify(rows.map((r) => JSON.parse(r.data)));
64
+ }
65
+
66
+ const now = new Date();
67
+ const templateId = randomUUID();
68
+
69
+ db.insert(userTableTemplates)
70
+ .values({
71
+ id: templateId,
72
+ name,
73
+ description: description ?? table.description,
74
+ category: (category as "business" | "personal" | "pm" | "finance" | "content") ?? "personal",
75
+ columnSchema: table.columnSchema,
76
+ sampleData,
77
+ scope: "user",
78
+ icon: null,
79
+ createdAt: now,
80
+ updatedAt: now,
81
+ })
82
+ .run();
83
+
84
+ return NextResponse.json({ id: templateId, name }, { status: 201 });
85
+ } catch (err) {
86
+ console.error("[tables] POST templates error:", err);
87
+ return NextResponse.json(
88
+ { error: "Failed to save template" },
89
+ { status: 500 }
90
+ );
91
+ }
92
+ }
@@ -1,6 +1,7 @@
1
1
  import { ProvidersAndRuntimesSection } from "@/components/settings/providers-runtimes-section";
2
2
  import { PermissionsSections } from "@/components/settings/permissions-sections";
3
3
  import { DataManagementSection } from "@/components/settings/data-management-section";
4
+ import { DatabaseSnapshotsSection } from "@/components/settings/database-snapshots-section";
4
5
  import { BudgetGuardrailsSection } from "@/components/settings/budget-guardrails-section";
5
6
  import { ChatSettingsSection } from "@/components/settings/chat-settings-section";
6
7
  import { RuntimeTimeoutSection } from "@/components/settings/runtime-timeout-section";
@@ -27,6 +28,7 @@ export default function SettingsPage() {
27
28
  <ChannelsSection />
28
29
  <BudgetGuardrailsSection />
29
30
  <PermissionsSections />
31
+ <DatabaseSnapshotsSection />
30
32
  <DataManagementSection />
31
33
  </div>
32
34
  </PageShell>
@@ -0,0 +1,67 @@
1
+ import { notFound } from "next/navigation";
2
+ import { db } from "@/lib/db";
3
+ import { projects } from "@/lib/db/schema";
4
+ import { eq } from "drizzle-orm";
5
+ import { getTable, listRows } from "@/lib/data/tables";
6
+ import { PageShell } from "@/components/shared/page-shell";
7
+ import { TableDetailTabs } from "@/components/tables/table-detail-tabs";
8
+ import { evaluateComputedColumns } from "@/lib/tables/computed";
9
+ import type { ColumnDef } from "@/lib/tables/types";
10
+
11
+ export const dynamic = "force-dynamic";
12
+
13
+ interface Props {
14
+ params: Promise<{ id: string }>;
15
+ }
16
+
17
+ export default async function TableDetailPage({ params }: Props) {
18
+ const { id } = await params;
19
+ const table = await getTable(id);
20
+
21
+ if (!table) {
22
+ notFound();
23
+ }
24
+
25
+ let columns: ColumnDef[] = [];
26
+ try {
27
+ columns = JSON.parse(table.columnSchema) as ColumnDef[];
28
+ } catch {
29
+ columns = [];
30
+ }
31
+
32
+ // Fetch project name if table is linked to a project
33
+ let projectName: string | null = null;
34
+ if (table.projectId) {
35
+ const project = db
36
+ .select({ name: projects.name })
37
+ .from(projects)
38
+ .where(eq(projects.id, table.projectId))
39
+ .get();
40
+ projectName = project?.name ?? null;
41
+ }
42
+
43
+ const rawRows = await listRows(id, { limit: 500 });
44
+ const rows = evaluateComputedColumns(columns, rawRows);
45
+
46
+ return (
47
+ <PageShell
48
+ title={table.name}
49
+ description={table.description ?? undefined}
50
+ backHref="/tables"
51
+ backLabel="Tables"
52
+ >
53
+ <TableDetailTabs
54
+ tableId={id}
55
+ columns={columns}
56
+ initialRows={rows}
57
+ tableMeta={{
58
+ source: table.source,
59
+ projectName,
60
+ rowCount: table.rowCount,
61
+ createdAt: table.createdAt ? new Date(table.createdAt).toISOString() : null,
62
+ updatedAt: table.updatedAt ? new Date(table.updatedAt).toISOString() : null,
63
+ }}
64
+ />
65
+ </PageShell>
66
+ );
67
+ }
@@ -0,0 +1,21 @@
1
+ import { listTables } from "@/lib/data/tables";
2
+ import { db } from "@/lib/db";
3
+ import { projects } from "@/lib/db/schema";
4
+ import { TableBrowser } from "@/components/tables/table-browser";
5
+ import { PageShell } from "@/components/shared/page-shell";
6
+
7
+ export const dynamic = "force-dynamic";
8
+
9
+ export default async function TablesPage() {
10
+ const tables = await listTables();
11
+
12
+ const projectList = await db
13
+ .select({ id: projects.id, name: projects.name })
14
+ .from(projects);
15
+
16
+ return (
17
+ <PageShell title="Tables">
18
+ <TableBrowser initialTables={tables} projects={projectList} />
19
+ </PageShell>
20
+ );
21
+ }
@@ -0,0 +1,19 @@
1
+ import { listTemplates } from "@/lib/data/tables";
2
+ import { PageShell } from "@/components/shared/page-shell";
3
+ import { TableTemplateGallery } from "@/components/tables/table-template-gallery";
4
+
5
+ export const dynamic = "force-dynamic";
6
+
7
+ export default async function TableTemplatesPage() {
8
+ const templates = await listTemplates();
9
+
10
+ return (
11
+ <PageShell
12
+ title="Table Templates"
13
+ backHref="/tables"
14
+ backLabel="Tables"
15
+ >
16
+ <TableTemplateGallery templates={templates} />
17
+ </PageShell>
18
+ );
19
+ }
@@ -0,0 +1,139 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Button } from "@/components/ui/button";
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { Table2, ChevronDown, ChevronUp, ExternalLink } from "lucide-react";
7
+ import Link from "next/link";
8
+
9
+ interface ChatTableResultProps {
10
+ tableId: string;
11
+ tableName: string;
12
+ columns: string[];
13
+ rows: Array<Record<string, unknown>>;
14
+ totalRows?: number;
15
+ aggregation?: {
16
+ operation: string;
17
+ column: string;
18
+ result: number;
19
+ count: number;
20
+ };
21
+ }
22
+
23
+ const MAX_VISIBLE_ROWS = 10;
24
+
25
+ export function ChatTableResult({
26
+ tableId,
27
+ tableName,
28
+ columns,
29
+ rows,
30
+ totalRows,
31
+ aggregation,
32
+ }: ChatTableResultProps) {
33
+ const [expanded, setExpanded] = useState(false);
34
+ const visibleRows = expanded ? rows : rows.slice(0, MAX_VISIBLE_ROWS);
35
+ const hasMore = rows.length > MAX_VISIBLE_ROWS;
36
+
37
+ if (aggregation) {
38
+ return (
39
+ <div className="rounded-lg border p-3 my-2 space-y-2">
40
+ <div className="flex items-center gap-2">
41
+ <Table2 className="h-4 w-4 text-muted-foreground" />
42
+ <span className="text-sm font-medium">{tableName}</span>
43
+ <Badge variant="secondary" className="text-xs">
44
+ {aggregation.operation}
45
+ </Badge>
46
+ </div>
47
+ <div className="flex items-baseline gap-2">
48
+ <span className="text-2xl font-semibold tabular-nums">
49
+ {typeof aggregation.result === "number"
50
+ ? aggregation.result.toLocaleString()
51
+ : String(aggregation.result)}
52
+ </span>
53
+ <span className="text-xs text-muted-foreground">
54
+ {aggregation.column} · {aggregation.count} rows
55
+ </span>
56
+ </div>
57
+ </div>
58
+ );
59
+ }
60
+
61
+ return (
62
+ <div className="rounded-lg border my-2 overflow-hidden">
63
+ <div className="flex items-center justify-between px-3 py-2 bg-muted/50">
64
+ <div className="flex items-center gap-2">
65
+ <Table2 className="h-4 w-4 text-muted-foreground" />
66
+ <span className="text-sm font-medium">{tableName}</span>
67
+ <Badge variant="secondary" className="text-xs">
68
+ {totalRows ?? rows.length} row{(totalRows ?? rows.length) !== 1 ? "s" : ""}
69
+ </Badge>
70
+ </div>
71
+ <Link
72
+ href={`/tables/${tableId}`}
73
+ className="text-xs text-muted-foreground hover:text-foreground flex items-center gap-1"
74
+ >
75
+ Open <ExternalLink className="h-3 w-3" />
76
+ </Link>
77
+ </div>
78
+
79
+ {columns.length > 0 && visibleRows.length > 0 && (
80
+ <div className="overflow-x-auto">
81
+ <table className="w-full text-xs">
82
+ <thead>
83
+ <tr className="border-b bg-muted/30">
84
+ {columns.slice(0, 8).map((col) => (
85
+ <th key={col} className="px-3 py-1.5 text-left font-medium text-muted-foreground">
86
+ {col}
87
+ </th>
88
+ ))}
89
+ {columns.length > 8 && (
90
+ <th className="px-3 py-1.5 text-left font-medium text-muted-foreground">
91
+ +{columns.length - 8} more
92
+ </th>
93
+ )}
94
+ </tr>
95
+ </thead>
96
+ <tbody>
97
+ {visibleRows.map((row, i) => (
98
+ <tr key={i} className="border-b last:border-0">
99
+ {columns.slice(0, 8).map((col) => (
100
+ <td key={col} className="px-3 py-1.5 max-w-[200px] truncate">
101
+ {row[col] == null ? (
102
+ <span className="text-muted-foreground/40">—</span>
103
+ ) : (
104
+ String(row[col])
105
+ )}
106
+ </td>
107
+ ))}
108
+ </tr>
109
+ ))}
110
+ </tbody>
111
+ </table>
112
+ </div>
113
+ )}
114
+
115
+ {hasMore && (
116
+ <div className="px-3 py-2 border-t">
117
+ <Button
118
+ variant="ghost"
119
+ size="sm"
120
+ className="w-full text-xs"
121
+ onClick={() => setExpanded(!expanded)}
122
+ >
123
+ {expanded ? (
124
+ <>
125
+ <ChevronUp className="h-3 w-3 mr-1" />
126
+ Show less
127
+ </>
128
+ ) : (
129
+ <>
130
+ <ChevronDown className="h-3 w-3 mr-1" />
131
+ Show all {rows.length} rows
132
+ </>
133
+ )}
134
+ </Button>
135
+ </div>
136
+ )}
137
+ </div>
138
+ );
139
+ }
@@ -89,7 +89,7 @@ export function DocumentBrowser({
89
89
  let deleted = 0;
90
90
  for (const id of selected) {
91
91
  try {
92
- const res = await fetch(`/api/documents/${id}`, { method: "DELETE" });
92
+ const res = await fetch(`/api/documents/${id}?cascadeDelete=true`, { method: "DELETE" });
93
93
  if (res.ok) deleted++;
94
94
  } catch {
95
95
  // Continue with remaining
@@ -98,35 +98,11 @@ export function ProjectFormSheet({
98
98
  }, [mode, project, open]);
99
99
 
100
100
  const handleDocPickerConfirm = useCallback(
101
- (ids: string[]) => {
101
+ (ids: string[], meta: Array<{ id: string; originalName: string; mimeType: string; size: number }>) => {
102
102
  setSelectedDocIds(new Set(ids));
103
- const newIds = ids.filter(
104
- (id) => !selectedDocs.some((d) => d.id === id)
105
- );
106
- if (newIds.length > 0) {
107
- const params = new URLSearchParams({ status: "ready" });
108
- if (project?.id) params.set("projectId", project.id);
109
- fetch(`/api/documents?${params}`)
110
- .then((r) => r.json())
111
- .then((allDocs: Array<Record<string, unknown>>) => {
112
- const idSet = new Set(ids);
113
- setSelectedDocs(
114
- allDocs
115
- .filter((d) => idSet.has(d.id as string))
116
- .map((d) => ({
117
- id: d.id as string,
118
- originalName: d.originalName as string,
119
- mimeType: d.mimeType as string,
120
- size: d.size as number,
121
- }))
122
- );
123
- })
124
- .catch(() => {});
125
- } else {
126
- setSelectedDocs((prev) => prev.filter((d) => ids.includes(d.id)));
127
- }
103
+ setSelectedDocs(meta);
128
104
  },
129
- [project?.id, selectedDocs]
105
+ []
130
106
  );
131
107
 
132
108
  async function handleSubmit(e: React.FormEvent) {
@@ -211,35 +211,11 @@ export function ScheduleForm({
211
211
  const [pickerOpen, setPickerOpen] = useState(false);
212
212
 
213
213
  const handleDocPickerConfirm = useCallback(
214
- (ids: string[]) => {
214
+ (ids: string[], meta: Array<{ id: string; originalName: string; mimeType: string; size: number }>) => {
215
215
  setSelectedDocIds(new Set(ids));
216
- const newIds = ids.filter(
217
- (id) => !selectedDocs.some((d) => d.id === id)
218
- );
219
- if (newIds.length > 0) {
220
- const params = new URLSearchParams({ status: "ready" });
221
- if (projectId) params.set("projectId", projectId);
222
- fetch(`/api/documents?${params}`)
223
- .then((r) => r.json())
224
- .then((allDocs: Array<Record<string, unknown>>) => {
225
- const idSet = new Set(ids);
226
- setSelectedDocs(
227
- allDocs
228
- .filter((d) => idSet.has(d.id as string))
229
- .map((d) => ({
230
- id: d.id as string,
231
- originalName: d.originalName as string,
232
- mimeType: d.mimeType as string,
233
- size: d.size as number,
234
- }))
235
- );
236
- })
237
- .catch(() => {});
238
- } else {
239
- setSelectedDocs((prev) => prev.filter((d) => ids.includes(d.id)));
240
- }
216
+ setSelectedDocs(meta);
241
217
  },
242
- [projectId, selectedDocs]
218
+ []
243
219
  );
244
220
 
245
221
  useEffect(() => {
@@ -758,6 +734,8 @@ export function ScheduleForm({
758
734
  onConfirm={handleDocPickerConfirm}
759
735
  groupBy="source"
760
736
  title="Select Context Documents"
737
+ allowCrossProject
738
+ selectedDocumentMeta={selectedDocs}
761
739
  />
762
740
 
763
741
  {/* Runtime */}
@@ -28,7 +28,7 @@ export function DataManagementSection() {
28
28
  if (data.success) {
29
29
  const d = data.deleted;
30
30
  toast.success(
31
- `Cleared ${d.projects} projects, ${d.tasks} tasks, ${d.workflows} workflows, ${d.schedules} schedules, ${d.documents} documents, ${d.conversations} conversations, ${d.chatMessages} messages, ${d.learnedContext} learned context, ${d.views} views, ${d.agentLogs} logs, ${d.notifications} notifications, ${d.sampleProfiles} sample profiles, ${d.files} files`
31
+ `Cleared ${d.projects} projects, ${d.tasks} tasks, ${d.workflows} workflows, ${d.schedules} schedules, ${d.documents} documents, ${d.conversations} conversations, ${d.chatMessages} messages, ${d.learnedContext} learned context, ${d.views} views, ${d.usageLedger} usage entries, ${d.agentLogs} logs, ${d.notifications} notifications, ${d.sampleProfiles} sample profiles, ${d.files} files`
32
32
  );
33
33
  } else {
34
34
  toast.error(`Clear failed: ${data.error}`);
@@ -48,7 +48,7 @@ export function DataManagementSection() {
48
48
  if (data.success) {
49
49
  const s = data.seeded;
50
50
  toast.success(
51
- `Seeded ${s.profiles} profiles, ${s.projects} projects, ${s.tasks} tasks, ${s.workflows} workflows, ${s.schedules} schedules, ${s.documents} documents, ${s.conversations} conversations, ${s.chatMessages} messages, ${s.learnedContext} learned context, ${s.views} views, ${s.agentLogs} logs, ${s.notifications} notifications`
51
+ `Seeded ${s.profiles} profiles, ${s.projects} projects, ${s.tasks} tasks, ${s.workflows} workflows, ${s.schedules} schedules, ${s.documents} documents, ${s.userTables} tables (${s.userTableRows} rows), ${s.conversations} conversations, ${s.chatMessages} messages, ${s.usageLedger} usage entries, ${s.learnedContext} learned context, ${s.views} views, ${s.profileTestResults} test results, ${s.repoImports} repo imports, ${s.agentLogs} logs, ${s.notifications} notifications`
52
52
  );
53
53
  } else {
54
54
  toast.error(`Seed failed: ${data.error}`);
@@ -74,9 +74,10 @@ export function DataManagementSection() {
74
74
  <div className="flex items-center gap-2">
75
75
  <p className="text-sm text-muted-foreground">
76
76
  Delete all projects, tasks, workflows, schedules, documents,
77
- conversations, chat messages, learned context, saved views,
78
- agent logs, notifications, seeded sample profiles, and uploaded
79
- files. Authentication settings are preserved.
77
+ conversations, chat messages, usage ledger, learned context,
78
+ saved views, agent logs, notifications, seeded sample profiles,
79
+ and uploaded files.{" "}
80
+ <strong>Database snapshots and authentication settings are preserved.</strong>
80
81
  </p>
81
82
  <Badge variant="destructive" className="shrink-0">Irreversible</Badge>
82
83
  </div>
@@ -98,11 +99,15 @@ export function DataManagementSection() {
98
99
 
99
100
  <div className="space-y-3">
100
101
  <p className="text-sm text-muted-foreground">
101
- Populate with 3 custom profiles, 5 realistic projects, 25 tasks
102
- across varied statuses, 5 workflows, 4 schedules, 12 documents
103
- (XLSX, PDF, DOCX, PPTX), 3 conversations with chat history,
104
- learned context, saved views, profile test results, repo imports,
105
- agent logs, and notifications. Existing data is cleared first.
102
+ Populate with 5 agent profiles, 8 projects across 3 personas
103
+ (solo founder, agency, PE ops), 48 tasks with agent profiles and
104
+ source types, 8 workflows (sequence, checkpoint, planner-executor),
105
+ 8 schedules (including 3 heartbeat monitors), 18 markdown documents
106
+ (input and output), 6 data tables with rows (pipeline, content,
107
+ health scores, KPIs, listings, campaigns), 6 conversations with
108
+ 45 messages, 45 usage ledger entries across 3 runtimes, learned
109
+ context, 6 saved views, 4 profile test results, 3 repo imports,
110
+ agent logs, and 28 notifications. Existing data is cleared first.
106
111
  </p>
107
112
  <Button
108
113
  variant="outline"
@@ -124,7 +129,7 @@ export function DataManagementSection() {
124
129
  open={clearOpen}
125
130
  onOpenChange={setClearOpen}
126
131
  title="Clear all data?"
127
- description="This will permanently delete all projects, tasks, workflows, schedules, documents, conversations, chat messages, learned context, saved views, agent logs, notifications, seeded sample profiles, and uploaded files. Authentication settings will be preserved. This action cannot be undone."
132
+ description="This will permanently delete all projects, tasks, workflows, schedules, documents, conversations, chat messages, usage ledger, learned context, saved views, agent logs, notifications, seeded sample profiles, and uploaded files. Database snapshots and authentication settings will be preserved. This action cannot be undone."
128
133
  confirmLabel="Clear All Data"
129
134
  onConfirm={handleClear}
130
135
  destructive
@@ -134,7 +139,7 @@ export function DataManagementSection() {
134
139
  open={seedOpen}
135
140
  onOpenChange={setSeedOpen}
136
141
  title="Seed sample data?"
137
- description="This will clear all existing data first, then populate with 3 custom profiles, 5 projects, 25 tasks, 5 workflows, 4 schedules, 12 documents (XLSX, PDF, DOCX, PPTX), 3 conversations with chat history, learned context, saved views, profile test results, repo imports, agent logs, and notifications. Any current data will be lost."
142
+ description="This will clear all existing data first, then populate with 5 agent profiles, 8 projects across 3 personas (solo founder, agency, PE ops), 48 tasks, 8 workflows, 8 schedules (3 heartbeat), 18 markdown documents, 6 data tables with rows, 6 conversations, 45 usage entries across 3 runtimes, learned context, saved views, profile test results, repo imports, agent logs, and 28 notifications. Any current data will be lost."
138
143
  confirmLabel="Seed Data"
139
144
  onConfirm={handleSeed}
140
145
  />