stagent 0.6.2 → 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.
- package/README.md +21 -2
- package/dist/cli.js +272 -1
- package/docs/.coverage-gaps.json +66 -16
- package/docs/.last-generated +1 -1
- package/docs/features/dashboard-kanban.md +13 -7
- package/docs/features/settings.md +15 -3
- package/docs/features/tables.md +122 -0
- package/docs/index.md +3 -2
- package/docs/journeys/developer.md +26 -16
- package/docs/journeys/personal-use.md +23 -9
- package/docs/journeys/power-user.md +40 -14
- package/docs/journeys/work-use.md +43 -15
- package/docs/manifest.json +27 -17
- package/package.json +3 -2
- package/src/app/api/chat/entities/search/route.ts +12 -3
- package/src/app/api/documents/[id]/route.ts +5 -1
- package/src/app/api/documents/[id]/versions/route.ts +53 -0
- package/src/app/api/documents/route.ts +5 -1
- package/src/app/api/projects/[id]/documents/route.ts +124 -0
- package/src/app/api/projects/[id]/route.ts +72 -3
- package/src/app/api/projects/__tests__/delete-project.test.ts +13 -0
- package/src/app/api/schedules/route.ts +19 -1
- package/src/app/api/snapshots/[id]/restore/route.ts +62 -0
- package/src/app/api/snapshots/[id]/route.ts +44 -0
- package/src/app/api/snapshots/route.ts +54 -0
- package/src/app/api/snapshots/settings/route.ts +67 -0
- package/src/app/api/tables/[id]/charts/[chartId]/route.ts +89 -0
- package/src/app/api/tables/[id]/charts/route.ts +72 -0
- package/src/app/api/tables/[id]/columns/route.ts +70 -0
- package/src/app/api/tables/[id]/export/route.ts +94 -0
- package/src/app/api/tables/[id]/history/route.ts +15 -0
- package/src/app/api/tables/[id]/import/route.ts +111 -0
- package/src/app/api/tables/[id]/route.ts +86 -0
- package/src/app/api/tables/[id]/rows/[rowId]/history/route.ts +32 -0
- package/src/app/api/tables/[id]/rows/[rowId]/route.ts +51 -0
- package/src/app/api/tables/[id]/rows/route.ts +101 -0
- package/src/app/api/tables/[id]/triggers/[triggerId]/route.ts +65 -0
- package/src/app/api/tables/[id]/triggers/route.ts +122 -0
- package/src/app/api/tables/route.ts +65 -0
- package/src/app/api/tables/templates/route.ts +92 -0
- package/src/app/api/tasks/[id]/route.ts +37 -2
- package/src/app/api/tasks/[id]/siblings/route.ts +48 -0
- package/src/app/api/tasks/route.ts +8 -9
- package/src/app/api/workflows/[id]/documents/route.ts +209 -0
- package/src/app/api/workflows/[id]/execute/route.ts +6 -2
- package/src/app/api/workflows/[id]/route.ts +16 -3
- package/src/app/api/workflows/[id]/status/route.ts +18 -2
- package/src/app/api/workflows/route.ts +13 -2
- package/src/app/documents/page.tsx +5 -1
- package/src/app/layout.tsx +0 -1
- package/src/app/manifest.ts +3 -3
- package/src/app/projects/[id]/page.tsx +62 -2
- package/src/app/settings/page.tsx +2 -0
- package/src/app/tables/[id]/page.tsx +67 -0
- package/src/app/tables/page.tsx +21 -0
- package/src/app/tables/templates/page.tsx +19 -0
- package/src/components/chat/chat-table-result.tsx +139 -0
- package/src/components/documents/document-browser.tsx +1 -1
- package/src/components/documents/document-chip-bar.tsx +17 -1
- package/src/components/documents/document-detail-view.tsx +51 -0
- package/src/components/documents/document-grid.tsx +5 -0
- package/src/components/documents/document-table.tsx +4 -0
- package/src/components/documents/types.ts +3 -0
- package/src/components/projects/project-form-sheet.tsx +109 -2
- package/src/components/schedules/schedule-form.tsx +91 -1
- package/src/components/settings/data-management-section.tsx +17 -12
- package/src/components/settings/database-snapshots-section.tsx +469 -0
- package/src/components/shared/app-sidebar.tsx +2 -0
- package/src/components/shared/document-picker-sheet.tsx +486 -0
- package/src/components/tables/table-browser.tsx +234 -0
- package/src/components/tables/table-cell-editor.tsx +226 -0
- package/src/components/tables/table-chart-builder.tsx +288 -0
- package/src/components/tables/table-chart-view.tsx +146 -0
- package/src/components/tables/table-column-header.tsx +103 -0
- package/src/components/tables/table-column-sheet.tsx +331 -0
- package/src/components/tables/table-create-sheet.tsx +240 -0
- package/src/components/tables/table-detail-sheet.tsx +144 -0
- package/src/components/tables/table-detail-tabs.tsx +278 -0
- package/src/components/tables/table-grid.tsx +61 -0
- package/src/components/tables/table-history-tab.tsx +148 -0
- package/src/components/tables/table-import-wizard.tsx +542 -0
- package/src/components/tables/table-list-table.tsx +95 -0
- package/src/components/tables/table-relation-combobox.tsx +217 -0
- package/src/components/tables/table-spreadsheet.tsx +499 -0
- package/src/components/tables/table-template-gallery.tsx +162 -0
- package/src/components/tables/table-template-preview.tsx +219 -0
- package/src/components/tables/table-toolbar.tsx +79 -0
- package/src/components/tables/table-triggers-tab.tsx +446 -0
- package/src/components/tables/types.ts +6 -0
- package/src/components/tables/use-spreadsheet-keys.ts +171 -0
- package/src/components/tables/utils.ts +29 -0
- package/src/components/tasks/task-card.tsx +8 -1
- package/src/components/tasks/task-create-panel.tsx +111 -14
- package/src/components/tasks/task-detail-view.tsx +47 -0
- package/src/components/tasks/task-edit-dialog.tsx +103 -2
- package/src/components/workflows/workflow-form-view.tsx +207 -7
- package/src/components/workflows/workflow-kanban-card.tsx +8 -1
- package/src/components/workflows/workflow-list.tsx +90 -45
- package/src/components/workflows/workflow-status-view.tsx +168 -23
- package/src/instrumentation.ts +3 -0
- package/src/lib/__tests__/npx-process-cwd.test.ts +17 -2
- package/src/lib/agents/__tests__/claude-agent.test.ts +5 -1
- package/src/lib/agents/claude-agent.ts +3 -1
- package/src/lib/agents/profiles/registry.ts +6 -3
- package/src/lib/agents/runtime/anthropic-direct.ts +29 -0
- package/src/lib/agents/runtime/openai-direct.ts +29 -0
- package/src/lib/book/__tests__/chapter-slugs.test.ts +80 -0
- package/src/lib/book/chapter-generator.ts +4 -19
- package/src/lib/book/chapter-mapping.ts +17 -0
- package/src/lib/book/content.ts +5 -16
- package/src/lib/book/update-detector.ts +3 -16
- package/src/lib/chat/engine.ts +1 -0
- package/src/lib/chat/stagent-tools.ts +2 -0
- package/src/lib/chat/system-prompt.ts +9 -1
- package/src/lib/chat/tool-catalog.ts +35 -0
- package/src/lib/chat/tools/settings-tools.ts +109 -0
- package/src/lib/chat/tools/table-tools.ts +955 -0
- package/src/lib/chat/tools/workflow-tools.ts +145 -2
- package/src/lib/constants/table-status.ts +68 -0
- package/src/lib/data/__tests__/clear.test.ts +1 -1
- package/src/lib/data/clear.ts +57 -0
- package/src/lib/data/seed-data/__tests__/profiles.test.ts +28 -23
- package/src/lib/data/seed-data/conversations.ts +350 -42
- package/src/lib/data/seed-data/documents.ts +564 -591
- package/src/lib/data/seed-data/learned-context.ts +101 -22
- package/src/lib/data/seed-data/notifications.ts +344 -70
- package/src/lib/data/seed-data/profile-test-results.ts +92 -11
- package/src/lib/data/seed-data/profiles.ts +144 -46
- package/src/lib/data/seed-data/projects.ts +50 -18
- package/src/lib/data/seed-data/repo-imports.ts +28 -13
- package/src/lib/data/seed-data/schedules.ts +208 -41
- package/src/lib/data/seed-data/table-templates.ts +234 -0
- package/src/lib/data/seed-data/tasks.ts +614 -116
- package/src/lib/data/seed-data/usage-ledger.ts +182 -103
- package/src/lib/data/seed-data/user-tables.ts +203 -0
- package/src/lib/data/seed-data/views.ts +52 -7
- package/src/lib/data/seed-data/workflows.ts +231 -84
- package/src/lib/data/seed.ts +55 -14
- package/src/lib/data/tables.ts +417 -0
- package/src/lib/db/bootstrap.ts +275 -0
- package/src/lib/db/index.ts +9 -0
- package/src/lib/db/migrations/0016_add_workflow_document_inputs.sql +13 -0
- package/src/lib/db/migrations/0017_add_document_picker_tables.sql +25 -0
- package/src/lib/db/migrations/0018_add_workflow_run_number.sql +2 -0
- package/src/lib/db/migrations/0019_add_tables_feature.sql +160 -0
- package/src/lib/db/migrations/0020_add_table_triggers.sql +19 -0
- package/src/lib/db/migrations/0021_add_row_history.sql +15 -0
- package/src/lib/db/schema.ts +445 -0
- package/src/lib/docs/reader.ts +2 -3
- package/src/lib/documents/context-builder.ts +75 -2
- package/src/lib/documents/document-resolver.ts +119 -0
- package/src/lib/documents/processors/spreadsheet.ts +2 -1
- package/src/lib/schedules/scheduler.ts +31 -1
- package/src/lib/snapshots/auto-backup.ts +132 -0
- package/src/lib/snapshots/retention.ts +64 -0
- package/src/lib/snapshots/snapshot-manager.ts +429 -0
- package/src/lib/tables/computed.ts +61 -0
- package/src/lib/tables/context-builder.ts +139 -0
- package/src/lib/tables/formula-engine.ts +415 -0
- package/src/lib/tables/history.ts +115 -0
- package/src/lib/tables/import.ts +343 -0
- package/src/lib/tables/query-builder.ts +152 -0
- package/src/lib/tables/trigger-evaluator.ts +146 -0
- package/src/lib/tables/types.ts +141 -0
- package/src/lib/tables/validation.ts +119 -0
- package/src/lib/utils/app-root.ts +20 -0
- package/src/lib/utils/stagent-paths.ts +20 -0
- package/src/lib/validators/__tests__/task.test.ts +43 -10
- package/src/lib/validators/task.ts +7 -1
- package/src/lib/workflows/blueprints/registry.ts +3 -3
- package/src/lib/workflows/engine.ts +24 -8
- package/src/lib/workflows/types.ts +14 -0
- package/tsconfig.json +3 -1
- package/public/icon.svg +0 -13
- package/src/components/tasks/file-upload.tsx +0 -120
- /package/docs/features/{playbook.md → user-guide.md} +0 -0
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { defineTool } from "../tool-registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { db } from "@/lib/db";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
workflows,
|
|
6
|
+
tasks,
|
|
7
|
+
agentLogs,
|
|
8
|
+
notifications,
|
|
9
|
+
documents,
|
|
10
|
+
workflowDocumentInputs,
|
|
11
|
+
} from "@/lib/db/schema";
|
|
12
|
+
import { eq, and, desc, inArray, like } from "drizzle-orm";
|
|
6
13
|
import { ok, err, type ToolContext } from "./helpers";
|
|
7
14
|
|
|
8
15
|
const VALID_WORKFLOW_STATUSES = [
|
|
@@ -74,6 +81,12 @@ export function workflowTools(ctx: ToolContext) {
|
|
|
74
81
|
.describe(
|
|
75
82
|
'Workflow definition as JSON string. Must include "pattern" and "steps" array. Example: {"pattern":"sequence","steps":[{"name":"step1","prompt":"Do X","assignedAgent":"claude"}]}'
|
|
76
83
|
),
|
|
84
|
+
documentIds: z
|
|
85
|
+
.array(z.string())
|
|
86
|
+
.optional()
|
|
87
|
+
.describe(
|
|
88
|
+
"Optional array of document IDs from the project pool to attach as input context. These documents will be injected into all workflow steps at execution time."
|
|
89
|
+
),
|
|
77
90
|
},
|
|
78
91
|
async (args) => {
|
|
79
92
|
try {
|
|
@@ -103,6 +116,25 @@ export function workflowTools(ctx: ToolContext) {
|
|
|
103
116
|
updatedAt: now,
|
|
104
117
|
});
|
|
105
118
|
|
|
119
|
+
// Attach pool documents if provided
|
|
120
|
+
const attachedDocs: string[] = [];
|
|
121
|
+
if (args.documentIds && args.documentIds.length > 0) {
|
|
122
|
+
for (const docId of args.documentIds) {
|
|
123
|
+
try {
|
|
124
|
+
await db.insert(workflowDocumentInputs).values({
|
|
125
|
+
id: crypto.randomUUID(),
|
|
126
|
+
workflowId: id,
|
|
127
|
+
documentId: docId,
|
|
128
|
+
stepId: null,
|
|
129
|
+
createdAt: now,
|
|
130
|
+
});
|
|
131
|
+
attachedDocs.push(docId);
|
|
132
|
+
} catch {
|
|
133
|
+
// Skip duplicates or invalid doc IDs
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
106
138
|
const [workflow] = await db
|
|
107
139
|
.select()
|
|
108
140
|
.from(workflows)
|
|
@@ -115,6 +147,7 @@ export function workflowTools(ctx: ToolContext) {
|
|
|
115
147
|
projectId: workflow.projectId,
|
|
116
148
|
status: workflow.status,
|
|
117
149
|
createdAt: workflow.createdAt,
|
|
150
|
+
attachedDocuments: attachedDocs.length,
|
|
118
151
|
});
|
|
119
152
|
} catch (e) {
|
|
120
153
|
return err(e instanceof Error ? e.message : "Failed to create workflow");
|
|
@@ -354,5 +387,115 @@ export function workflowTools(ctx: ToolContext) {
|
|
|
354
387
|
}
|
|
355
388
|
}
|
|
356
389
|
),
|
|
390
|
+
|
|
391
|
+
defineTool(
|
|
392
|
+
"find_related_documents",
|
|
393
|
+
"Search for documents in the project pool that could be used as context for a workflow. Returns output documents from completed workflows and uploaded documents. Use this proactively when creating follow-up workflows to discover relevant context.",
|
|
394
|
+
{
|
|
395
|
+
projectId: z
|
|
396
|
+
.string()
|
|
397
|
+
.optional()
|
|
398
|
+
.describe("Project ID to search in. Omit to use the active project."),
|
|
399
|
+
query: z
|
|
400
|
+
.string()
|
|
401
|
+
.optional()
|
|
402
|
+
.describe("Search query to match against document names"),
|
|
403
|
+
direction: z
|
|
404
|
+
.enum(["input", "output"])
|
|
405
|
+
.optional()
|
|
406
|
+
.describe('Filter by direction. Use "output" to find documents produced by other workflows.'),
|
|
407
|
+
sourceWorkflowId: z
|
|
408
|
+
.string()
|
|
409
|
+
.optional()
|
|
410
|
+
.describe("Filter to documents produced by a specific workflow"),
|
|
411
|
+
limit: z
|
|
412
|
+
.number()
|
|
413
|
+
.optional()
|
|
414
|
+
.describe("Maximum number of documents to return (default: 20)"),
|
|
415
|
+
},
|
|
416
|
+
async (args) => {
|
|
417
|
+
try {
|
|
418
|
+
const effectiveProjectId = args.projectId ?? ctx.projectId ?? undefined;
|
|
419
|
+
if (!effectiveProjectId) {
|
|
420
|
+
return err("No project context — specify a projectId or set an active project");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const conditions = [
|
|
424
|
+
eq(documents.projectId, effectiveProjectId),
|
|
425
|
+
eq(documents.status, "ready"),
|
|
426
|
+
];
|
|
427
|
+
|
|
428
|
+
if (args.direction) {
|
|
429
|
+
conditions.push(eq(documents.direction, args.direction));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (args.query) {
|
|
433
|
+
conditions.push(like(documents.originalName, `%${args.query}%`));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (args.sourceWorkflowId) {
|
|
437
|
+
// Find task IDs belonging to the source workflow
|
|
438
|
+
const workflowTasks = await db
|
|
439
|
+
.select({ id: tasks.id })
|
|
440
|
+
.from(tasks)
|
|
441
|
+
.where(eq(tasks.workflowId, args.sourceWorkflowId));
|
|
442
|
+
|
|
443
|
+
const taskIds = workflowTasks.map((t) => t.id);
|
|
444
|
+
if (taskIds.length > 0) {
|
|
445
|
+
conditions.push(inArray(documents.taskId, taskIds));
|
|
446
|
+
} else {
|
|
447
|
+
return ok([]); // No tasks for this workflow
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const result = await db
|
|
452
|
+
.select({
|
|
453
|
+
id: documents.id,
|
|
454
|
+
originalName: documents.originalName,
|
|
455
|
+
mimeType: documents.mimeType,
|
|
456
|
+
size: documents.size,
|
|
457
|
+
direction: documents.direction,
|
|
458
|
+
category: documents.category,
|
|
459
|
+
status: documents.status,
|
|
460
|
+
taskId: documents.taskId,
|
|
461
|
+
createdAt: documents.createdAt,
|
|
462
|
+
})
|
|
463
|
+
.from(documents)
|
|
464
|
+
.where(and(...conditions))
|
|
465
|
+
.orderBy(desc(documents.createdAt))
|
|
466
|
+
.limit(args.limit ?? 20);
|
|
467
|
+
|
|
468
|
+
// Enrich with source workflow name
|
|
469
|
+
const enriched = await Promise.all(
|
|
470
|
+
result.map(async (doc) => {
|
|
471
|
+
let sourceWorkflowName: string | null = null;
|
|
472
|
+
if (doc.taskId) {
|
|
473
|
+
const [task] = await db
|
|
474
|
+
.select({ workflowId: tasks.workflowId })
|
|
475
|
+
.from(tasks)
|
|
476
|
+
.where(eq(tasks.id, doc.taskId));
|
|
477
|
+
if (task?.workflowId) {
|
|
478
|
+
const [wf] = await db
|
|
479
|
+
.select({ name: workflows.name })
|
|
480
|
+
.from(workflows)
|
|
481
|
+
.where(eq(workflows.id, task.workflowId));
|
|
482
|
+
sourceWorkflowName = wf?.name ?? null;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
...doc,
|
|
487
|
+
sourceWorkflow: sourceWorkflowName,
|
|
488
|
+
};
|
|
489
|
+
})
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
return ok(enriched);
|
|
493
|
+
} catch (e) {
|
|
494
|
+
return err(
|
|
495
|
+
e instanceof Error ? e.message : "Failed to find documents"
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
),
|
|
357
500
|
];
|
|
358
501
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const TABLE_SOURCES = [
|
|
2
|
+
"manual",
|
|
3
|
+
"imported",
|
|
4
|
+
"agent",
|
|
5
|
+
"template",
|
|
6
|
+
] as const;
|
|
7
|
+
|
|
8
|
+
export type TableSource = (typeof TABLE_SOURCES)[number];
|
|
9
|
+
|
|
10
|
+
export const COLUMN_DATA_TYPES = [
|
|
11
|
+
"text",
|
|
12
|
+
"number",
|
|
13
|
+
"date",
|
|
14
|
+
"boolean",
|
|
15
|
+
"select",
|
|
16
|
+
"url",
|
|
17
|
+
"email",
|
|
18
|
+
"relation",
|
|
19
|
+
"computed",
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
export type ColumnDataType = (typeof COLUMN_DATA_TYPES)[number];
|
|
23
|
+
|
|
24
|
+
export const TABLE_VIEW_TYPES = ["grid", "chart", "joined"] as const;
|
|
25
|
+
export type TableViewType = (typeof TABLE_VIEW_TYPES)[number];
|
|
26
|
+
|
|
27
|
+
export const RELATIONSHIP_TYPES = [
|
|
28
|
+
"one_to_one",
|
|
29
|
+
"one_to_many",
|
|
30
|
+
"many_to_many",
|
|
31
|
+
] as const;
|
|
32
|
+
export type RelationshipType = (typeof RELATIONSHIP_TYPES)[number];
|
|
33
|
+
|
|
34
|
+
export const TEMPLATE_CATEGORIES = [
|
|
35
|
+
"business",
|
|
36
|
+
"personal",
|
|
37
|
+
"pm",
|
|
38
|
+
"finance",
|
|
39
|
+
"content",
|
|
40
|
+
] as const;
|
|
41
|
+
export type TemplateCategory = (typeof TEMPLATE_CATEGORIES)[number];
|
|
42
|
+
|
|
43
|
+
export const IMPORT_STATUSES = ["pending", "completed", "failed"] as const;
|
|
44
|
+
export type ImportStatus = (typeof IMPORT_STATUSES)[number];
|
|
45
|
+
|
|
46
|
+
/** Badge variant mappings for table source */
|
|
47
|
+
export const tableSourceVariant: Record<
|
|
48
|
+
TableSource,
|
|
49
|
+
"default" | "secondary" | "destructive" | "outline" | "success"
|
|
50
|
+
> = {
|
|
51
|
+
manual: "outline",
|
|
52
|
+
imported: "secondary",
|
|
53
|
+
agent: "default",
|
|
54
|
+
template: "success",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Display labels for column data types */
|
|
58
|
+
export const columnTypeLabel: Record<ColumnDataType, string> = {
|
|
59
|
+
text: "Text",
|
|
60
|
+
number: "Number",
|
|
61
|
+
date: "Date",
|
|
62
|
+
boolean: "Checkbox",
|
|
63
|
+
select: "Select",
|
|
64
|
+
url: "URL",
|
|
65
|
+
email: "Email",
|
|
66
|
+
relation: "Relation",
|
|
67
|
+
computed: "Computed",
|
|
68
|
+
};
|
|
@@ -11,7 +11,7 @@ import * as schema from "@/lib/db/schema";
|
|
|
11
11
|
* corresponding db.delete() call to clear.ts in the correct FK-safe order.
|
|
12
12
|
*/
|
|
13
13
|
describe("clearAllData coverage", () => {
|
|
14
|
-
const INTENTIONALLY_PRESERVED = ["settings"];
|
|
14
|
+
const INTENTIONALLY_PRESERVED = ["settings", "snapshots"];
|
|
15
15
|
|
|
16
16
|
it("deletes every schema table (except settings)", () => {
|
|
17
17
|
const clearSource = readFileSync(
|
package/src/lib/data/clear.ts
CHANGED
|
@@ -25,6 +25,22 @@ import {
|
|
|
25
25
|
channelBindings,
|
|
26
26
|
channelConfigs,
|
|
27
27
|
agentMessages,
|
|
28
|
+
workflowDocumentInputs,
|
|
29
|
+
scheduleDocumentInputs,
|
|
30
|
+
projectDocumentDefaults,
|
|
31
|
+
userTables,
|
|
32
|
+
userTableColumns,
|
|
33
|
+
userTableRows,
|
|
34
|
+
userTableViews,
|
|
35
|
+
userTableRelationships,
|
|
36
|
+
userTableImports,
|
|
37
|
+
userTableTemplates,
|
|
38
|
+
userTableTriggers,
|
|
39
|
+
userTableRowHistory,
|
|
40
|
+
tableDocumentInputs,
|
|
41
|
+
taskTableInputs,
|
|
42
|
+
workflowTableInputs,
|
|
43
|
+
scheduleTableInputs,
|
|
28
44
|
} from "@/lib/db/schema";
|
|
29
45
|
import { readdirSync, unlinkSync, mkdirSync } from "fs";
|
|
30
46
|
import { join } from "path";
|
|
@@ -65,12 +81,37 @@ export function clearAllData() {
|
|
|
65
81
|
const agentMessagesDeleted = db.delete(agentMessages).run().changes;
|
|
66
82
|
const channelConfigsDeleted = db.delete(channelConfigs).run().changes;
|
|
67
83
|
|
|
84
|
+
// Snapshots are intentionally preserved — they are backups, not working data
|
|
85
|
+
|
|
68
86
|
const repoImportsDeleted = db.delete(repoImports).run().changes;
|
|
69
87
|
const profileTestResultsDeleted = db.delete(profileTestResults).run().changes;
|
|
70
88
|
const viewsDeleted = db.delete(views).run().changes;
|
|
71
89
|
const usageLedgerDeleted = db.delete(usageLedger).run().changes;
|
|
72
90
|
const logsDeleted = db.delete(agentLogs).run().changes;
|
|
73
91
|
const notificationsDeleted = db.delete(notifications).run().changes;
|
|
92
|
+
|
|
93
|
+
// Table junction tables — delete before user_tables, tasks, workflows, schedules
|
|
94
|
+
const tableDocInputsDeleted = db.delete(tableDocumentInputs).run().changes;
|
|
95
|
+
const taskTableInputsDeleted = db.delete(taskTableInputs).run().changes;
|
|
96
|
+
const workflowTableInputsDeleted = db.delete(workflowTableInputs).run().changes;
|
|
97
|
+
const scheduleTableInputsDeleted = db.delete(scheduleTableInputs).run().changes;
|
|
98
|
+
|
|
99
|
+
// Table children — delete before user_tables
|
|
100
|
+
const userTableImportsDeleted = db.delete(userTableImports).run().changes;
|
|
101
|
+
const userTableViewsDeleted = db.delete(userTableViews).run().changes;
|
|
102
|
+
const userTableRelationshipsDeleted = db.delete(userTableRelationships).run().changes;
|
|
103
|
+
const userTableRowsDeleted = db.delete(userTableRows).run().changes;
|
|
104
|
+
const userTableRowHistoryDeleted = db.delete(userTableRowHistory).run().changes;
|
|
105
|
+
const userTableTriggersDeleted = db.delete(userTableTriggers).run().changes;
|
|
106
|
+
const userTableColumnsDeleted = db.delete(userTableColumns).run().changes;
|
|
107
|
+
const userTablesDeleted = db.delete(userTables).run().changes;
|
|
108
|
+
const userTableTemplatesDeleted = db.delete(userTableTemplates).run().changes;
|
|
109
|
+
|
|
110
|
+
// Document junction tables — delete before documents, workflows, schedules, projects
|
|
111
|
+
const workflowDocInputsDeleted = db.delete(workflowDocumentInputs).run().changes;
|
|
112
|
+
const scheduleDocInputsDeleted = db.delete(scheduleDocumentInputs).run().changes;
|
|
113
|
+
const projectDocDefaultsDeleted = db.delete(projectDocumentDefaults).run().changes;
|
|
114
|
+
|
|
74
115
|
const documentsDeleted = db.delete(documents).run().changes;
|
|
75
116
|
const agentMemoryDeleted = db.delete(agentMemory).run().changes;
|
|
76
117
|
const learnedContextDeleted = db.delete(learnedContext).run().changes;
|
|
@@ -130,6 +171,22 @@ export function clearAllData() {
|
|
|
130
171
|
agentMessages: agentMessagesDeleted,
|
|
131
172
|
channelBindings: channelBindingsDeleted,
|
|
132
173
|
channelConfigs: channelConfigsDeleted,
|
|
174
|
+
workflowDocumentInputs: workflowDocInputsDeleted,
|
|
175
|
+
scheduleDocumentInputs: scheduleDocInputsDeleted,
|
|
176
|
+
projectDocumentDefaults: projectDocDefaultsDeleted,
|
|
177
|
+
userTables: userTablesDeleted,
|
|
178
|
+
userTableColumns: userTableColumnsDeleted,
|
|
179
|
+
userTableRows: userTableRowsDeleted,
|
|
180
|
+
userTableViews: userTableViewsDeleted,
|
|
181
|
+
userTableRelationships: userTableRelationshipsDeleted,
|
|
182
|
+
userTableImports: userTableImportsDeleted,
|
|
183
|
+
userTableTemplates: userTableTemplatesDeleted,
|
|
184
|
+
userTableTriggers: userTableTriggersDeleted,
|
|
185
|
+
userTableRowHistory: userTableRowHistoryDeleted,
|
|
186
|
+
tableDocumentInputs: tableDocInputsDeleted,
|
|
187
|
+
taskTableInputs: taskTableInputsDeleted,
|
|
188
|
+
workflowTableInputs: workflowTableInputsDeleted,
|
|
189
|
+
scheduleTableInputs: scheduleTableInputsDeleted,
|
|
133
190
|
files: filesDeleted,
|
|
134
191
|
screenshots: screenshotsDeleted,
|
|
135
192
|
};
|
|
@@ -39,27 +39,27 @@ describe("sample profile seeds", () => {
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
it("defines stable reserved sample profile ids", () => {
|
|
42
|
-
expect(SAMPLE_PROFILE_IDS).toHaveLength(
|
|
42
|
+
expect(SAMPLE_PROFILE_IDS).toHaveLength(5);
|
|
43
43
|
expect(SAMPLE_PROFILE_IDS.every((id) => id.startsWith("stagent-sample-"))).toBe(true);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
it("returns realistic sample profiles with skill markdown", () => {
|
|
47
47
|
const profiles = getSampleProfiles();
|
|
48
48
|
|
|
49
|
-
expect(profiles).toHaveLength(
|
|
49
|
+
expect(profiles).toHaveLength(5);
|
|
50
50
|
expect(profiles.map((profile) => profile.config.id)).toEqual(
|
|
51
51
|
Array.from(SAMPLE_PROFILE_IDS)
|
|
52
52
|
);
|
|
53
53
|
expect(profiles[0].skillMd).toContain("description:");
|
|
54
|
-
expect(profiles[1].config.tags).toContain("
|
|
55
|
-
expect(profiles[
|
|
54
|
+
expect(profiles[1].config.tags).toContain("SEO");
|
|
55
|
+
expect(profiles[4].config.domain).toBe("work");
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
it("creates missing sample profiles", () => {
|
|
59
59
|
const count = upsertSampleProfiles();
|
|
60
60
|
|
|
61
|
-
expect(count).toBe(
|
|
62
|
-
expect(createProfile).toHaveBeenCalledTimes(
|
|
61
|
+
expect(count).toBe(5);
|
|
62
|
+
expect(createProfile).toHaveBeenCalledTimes(5);
|
|
63
63
|
expect(updateProfile).not.toHaveBeenCalled();
|
|
64
64
|
});
|
|
65
65
|
|
|
@@ -71,14 +71,14 @@ describe("sample profile seeds", () => {
|
|
|
71
71
|
|
|
72
72
|
const count = upsertSampleProfiles();
|
|
73
73
|
|
|
74
|
-
expect(count).toBe(
|
|
74
|
+
expect(count).toBe(5);
|
|
75
75
|
expect(updateProfile).toHaveBeenCalledTimes(1);
|
|
76
76
|
expect(updateProfile).toHaveBeenCalledWith(
|
|
77
77
|
SAMPLE_PROFILE_IDS[0],
|
|
78
78
|
expect.objectContaining({ id: SAMPLE_PROFILE_IDS[0] }),
|
|
79
|
-
expect.stringContaining("
|
|
79
|
+
expect.stringContaining("GTM Launch Strategist")
|
|
80
80
|
);
|
|
81
|
-
expect(createProfile).toHaveBeenCalledTimes(
|
|
81
|
+
expect(createProfile).toHaveBeenCalledTimes(4);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
it("throws if a sample id collides with a built-in profile", () => {
|
|
@@ -92,11 +92,11 @@ describe("sample profile seeds", () => {
|
|
|
92
92
|
|
|
93
93
|
it("deletes only existing non-builtin sample profiles", () => {
|
|
94
94
|
getProfile.mockImplementation((id: string) =>
|
|
95
|
-
id === SAMPLE_PROFILE_IDS[0] || id === SAMPLE_PROFILE_IDS[
|
|
95
|
+
id === SAMPLE_PROFILE_IDS[0] || id === SAMPLE_PROFILE_IDS[4]
|
|
96
96
|
? ({ id } as AgentProfile)
|
|
97
97
|
: undefined
|
|
98
98
|
);
|
|
99
|
-
isBuiltin.mockImplementation((id: string) => id === SAMPLE_PROFILE_IDS[
|
|
99
|
+
isBuiltin.mockImplementation((id: string) => id === SAMPLE_PROFILE_IDS[4]);
|
|
100
100
|
|
|
101
101
|
const deleted = clearSampleProfiles();
|
|
102
102
|
|
|
@@ -109,33 +109,38 @@ describe("sample profile seeds", () => {
|
|
|
109
109
|
describe("schedule seeds", () => {
|
|
110
110
|
it("creates schedules tied to seeded projects and profile surfaces", () => {
|
|
111
111
|
const projectIds = [
|
|
112
|
-
"project-investments",
|
|
113
112
|
"project-launch",
|
|
114
|
-
"project-
|
|
115
|
-
"project-
|
|
116
|
-
"project-
|
|
113
|
+
"project-content",
|
|
114
|
+
"project-cs",
|
|
115
|
+
"project-tvp",
|
|
116
|
+
"project-greenleaf",
|
|
117
|
+
"project-medreach",
|
|
118
|
+
"project-revops",
|
|
119
|
+
"project-compliance",
|
|
117
120
|
];
|
|
118
121
|
|
|
119
122
|
const schedules = createSchedules(projectIds);
|
|
120
123
|
|
|
121
|
-
expect(schedules).toHaveLength(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
projectIds[2],
|
|
126
|
-
projectIds[4],
|
|
127
|
-
]);
|
|
124
|
+
expect(schedules).toHaveLength(8);
|
|
125
|
+
// Each project gets one schedule
|
|
126
|
+
expect(schedules.map((schedule) => schedule.projectId)).toEqual(projectIds);
|
|
127
|
+
// Active schedules have a nextFireAt
|
|
128
128
|
expect(
|
|
129
129
|
schedules.filter((schedule) => schedule.status === "active").every(
|
|
130
130
|
(schedule) => schedule.nextFireAt instanceof Date
|
|
131
131
|
)
|
|
132
132
|
).toBe(true);
|
|
133
|
+
// Non-active schedules have null nextFireAt
|
|
133
134
|
expect(
|
|
134
135
|
schedules.filter((schedule) => schedule.status !== "active").every(
|
|
135
136
|
(schedule) => schedule.nextFireAt === null
|
|
136
137
|
)
|
|
137
138
|
).toBe(true);
|
|
139
|
+
// At least one schedule uses a sample profile
|
|
138
140
|
expect(schedules.some((schedule) => schedule.agentProfile === SAMPLE_PROFILE_IDS[0])).toBe(true);
|
|
139
|
-
|
|
141
|
+
// At least one heartbeat schedule exists
|
|
142
|
+
expect(schedules.some((schedule) => schedule.type === "heartbeat")).toBe(true);
|
|
143
|
+
// At least one schedule has delivery channels
|
|
144
|
+
expect(schedules.some((schedule) => schedule.deliveryChannels !== null)).toBe(true);
|
|
140
145
|
});
|
|
141
146
|
});
|