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.
Files changed (176) hide show
  1. package/README.md +21 -2
  2. package/dist/cli.js +272 -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 -2
  15. package/src/app/api/chat/entities/search/route.ts +12 -3
  16. package/src/app/api/documents/[id]/route.ts +5 -1
  17. package/src/app/api/documents/[id]/versions/route.ts +53 -0
  18. package/src/app/api/documents/route.ts +5 -1
  19. package/src/app/api/projects/[id]/documents/route.ts +124 -0
  20. package/src/app/api/projects/[id]/route.ts +72 -3
  21. package/src/app/api/projects/__tests__/delete-project.test.ts +13 -0
  22. package/src/app/api/schedules/route.ts +19 -1
  23. package/src/app/api/snapshots/[id]/restore/route.ts +62 -0
  24. package/src/app/api/snapshots/[id]/route.ts +44 -0
  25. package/src/app/api/snapshots/route.ts +54 -0
  26. package/src/app/api/snapshots/settings/route.ts +67 -0
  27. package/src/app/api/tables/[id]/charts/[chartId]/route.ts +89 -0
  28. package/src/app/api/tables/[id]/charts/route.ts +72 -0
  29. package/src/app/api/tables/[id]/columns/route.ts +70 -0
  30. package/src/app/api/tables/[id]/export/route.ts +94 -0
  31. package/src/app/api/tables/[id]/history/route.ts +15 -0
  32. package/src/app/api/tables/[id]/import/route.ts +111 -0
  33. package/src/app/api/tables/[id]/route.ts +86 -0
  34. package/src/app/api/tables/[id]/rows/[rowId]/history/route.ts +32 -0
  35. package/src/app/api/tables/[id]/rows/[rowId]/route.ts +51 -0
  36. package/src/app/api/tables/[id]/rows/route.ts +101 -0
  37. package/src/app/api/tables/[id]/triggers/[triggerId]/route.ts +65 -0
  38. package/src/app/api/tables/[id]/triggers/route.ts +122 -0
  39. package/src/app/api/tables/route.ts +65 -0
  40. package/src/app/api/tables/templates/route.ts +92 -0
  41. package/src/app/api/tasks/[id]/route.ts +37 -2
  42. package/src/app/api/tasks/[id]/siblings/route.ts +48 -0
  43. package/src/app/api/tasks/route.ts +8 -9
  44. package/src/app/api/workflows/[id]/documents/route.ts +209 -0
  45. package/src/app/api/workflows/[id]/execute/route.ts +6 -2
  46. package/src/app/api/workflows/[id]/route.ts +16 -3
  47. package/src/app/api/workflows/[id]/status/route.ts +18 -2
  48. package/src/app/api/workflows/route.ts +13 -2
  49. package/src/app/documents/page.tsx +5 -1
  50. package/src/app/layout.tsx +0 -1
  51. package/src/app/manifest.ts +3 -3
  52. package/src/app/projects/[id]/page.tsx +62 -2
  53. package/src/app/settings/page.tsx +2 -0
  54. package/src/app/tables/[id]/page.tsx +67 -0
  55. package/src/app/tables/page.tsx +21 -0
  56. package/src/app/tables/templates/page.tsx +19 -0
  57. package/src/components/chat/chat-table-result.tsx +139 -0
  58. package/src/components/documents/document-browser.tsx +1 -1
  59. package/src/components/documents/document-chip-bar.tsx +17 -1
  60. package/src/components/documents/document-detail-view.tsx +51 -0
  61. package/src/components/documents/document-grid.tsx +5 -0
  62. package/src/components/documents/document-table.tsx +4 -0
  63. package/src/components/documents/types.ts +3 -0
  64. package/src/components/projects/project-form-sheet.tsx +109 -2
  65. package/src/components/schedules/schedule-form.tsx +91 -1
  66. package/src/components/settings/data-management-section.tsx +17 -12
  67. package/src/components/settings/database-snapshots-section.tsx +469 -0
  68. package/src/components/shared/app-sidebar.tsx +2 -0
  69. package/src/components/shared/document-picker-sheet.tsx +486 -0
  70. package/src/components/tables/table-browser.tsx +234 -0
  71. package/src/components/tables/table-cell-editor.tsx +226 -0
  72. package/src/components/tables/table-chart-builder.tsx +288 -0
  73. package/src/components/tables/table-chart-view.tsx +146 -0
  74. package/src/components/tables/table-column-header.tsx +103 -0
  75. package/src/components/tables/table-column-sheet.tsx +331 -0
  76. package/src/components/tables/table-create-sheet.tsx +240 -0
  77. package/src/components/tables/table-detail-sheet.tsx +144 -0
  78. package/src/components/tables/table-detail-tabs.tsx +278 -0
  79. package/src/components/tables/table-grid.tsx +61 -0
  80. package/src/components/tables/table-history-tab.tsx +148 -0
  81. package/src/components/tables/table-import-wizard.tsx +542 -0
  82. package/src/components/tables/table-list-table.tsx +95 -0
  83. package/src/components/tables/table-relation-combobox.tsx +217 -0
  84. package/src/components/tables/table-spreadsheet.tsx +499 -0
  85. package/src/components/tables/table-template-gallery.tsx +162 -0
  86. package/src/components/tables/table-template-preview.tsx +219 -0
  87. package/src/components/tables/table-toolbar.tsx +79 -0
  88. package/src/components/tables/table-triggers-tab.tsx +446 -0
  89. package/src/components/tables/types.ts +6 -0
  90. package/src/components/tables/use-spreadsheet-keys.ts +171 -0
  91. package/src/components/tables/utils.ts +29 -0
  92. package/src/components/tasks/task-card.tsx +8 -1
  93. package/src/components/tasks/task-create-panel.tsx +111 -14
  94. package/src/components/tasks/task-detail-view.tsx +47 -0
  95. package/src/components/tasks/task-edit-dialog.tsx +103 -2
  96. package/src/components/workflows/workflow-form-view.tsx +207 -7
  97. package/src/components/workflows/workflow-kanban-card.tsx +8 -1
  98. package/src/components/workflows/workflow-list.tsx +90 -45
  99. package/src/components/workflows/workflow-status-view.tsx +168 -23
  100. package/src/instrumentation.ts +3 -0
  101. package/src/lib/__tests__/npx-process-cwd.test.ts +17 -2
  102. package/src/lib/agents/__tests__/claude-agent.test.ts +5 -1
  103. package/src/lib/agents/claude-agent.ts +3 -1
  104. package/src/lib/agents/profiles/registry.ts +6 -3
  105. package/src/lib/agents/runtime/anthropic-direct.ts +29 -0
  106. package/src/lib/agents/runtime/openai-direct.ts +29 -0
  107. package/src/lib/book/__tests__/chapter-slugs.test.ts +80 -0
  108. package/src/lib/book/chapter-generator.ts +4 -19
  109. package/src/lib/book/chapter-mapping.ts +17 -0
  110. package/src/lib/book/content.ts +5 -16
  111. package/src/lib/book/update-detector.ts +3 -16
  112. package/src/lib/chat/engine.ts +1 -0
  113. package/src/lib/chat/stagent-tools.ts +2 -0
  114. package/src/lib/chat/system-prompt.ts +9 -1
  115. package/src/lib/chat/tool-catalog.ts +35 -0
  116. package/src/lib/chat/tools/settings-tools.ts +109 -0
  117. package/src/lib/chat/tools/table-tools.ts +955 -0
  118. package/src/lib/chat/tools/workflow-tools.ts +145 -2
  119. package/src/lib/constants/table-status.ts +68 -0
  120. package/src/lib/data/__tests__/clear.test.ts +1 -1
  121. package/src/lib/data/clear.ts +57 -0
  122. package/src/lib/data/seed-data/__tests__/profiles.test.ts +28 -23
  123. package/src/lib/data/seed-data/conversations.ts +350 -42
  124. package/src/lib/data/seed-data/documents.ts +564 -591
  125. package/src/lib/data/seed-data/learned-context.ts +101 -22
  126. package/src/lib/data/seed-data/notifications.ts +344 -70
  127. package/src/lib/data/seed-data/profile-test-results.ts +92 -11
  128. package/src/lib/data/seed-data/profiles.ts +144 -46
  129. package/src/lib/data/seed-data/projects.ts +50 -18
  130. package/src/lib/data/seed-data/repo-imports.ts +28 -13
  131. package/src/lib/data/seed-data/schedules.ts +208 -41
  132. package/src/lib/data/seed-data/table-templates.ts +234 -0
  133. package/src/lib/data/seed-data/tasks.ts +614 -116
  134. package/src/lib/data/seed-data/usage-ledger.ts +182 -103
  135. package/src/lib/data/seed-data/user-tables.ts +203 -0
  136. package/src/lib/data/seed-data/views.ts +52 -7
  137. package/src/lib/data/seed-data/workflows.ts +231 -84
  138. package/src/lib/data/seed.ts +55 -14
  139. package/src/lib/data/tables.ts +417 -0
  140. package/src/lib/db/bootstrap.ts +275 -0
  141. package/src/lib/db/index.ts +9 -0
  142. package/src/lib/db/migrations/0016_add_workflow_document_inputs.sql +13 -0
  143. package/src/lib/db/migrations/0017_add_document_picker_tables.sql +25 -0
  144. package/src/lib/db/migrations/0018_add_workflow_run_number.sql +2 -0
  145. package/src/lib/db/migrations/0019_add_tables_feature.sql +160 -0
  146. package/src/lib/db/migrations/0020_add_table_triggers.sql +19 -0
  147. package/src/lib/db/migrations/0021_add_row_history.sql +15 -0
  148. package/src/lib/db/schema.ts +445 -0
  149. package/src/lib/docs/reader.ts +2 -3
  150. package/src/lib/documents/context-builder.ts +75 -2
  151. package/src/lib/documents/document-resolver.ts +119 -0
  152. package/src/lib/documents/processors/spreadsheet.ts +2 -1
  153. package/src/lib/schedules/scheduler.ts +31 -1
  154. package/src/lib/snapshots/auto-backup.ts +132 -0
  155. package/src/lib/snapshots/retention.ts +64 -0
  156. package/src/lib/snapshots/snapshot-manager.ts +429 -0
  157. package/src/lib/tables/computed.ts +61 -0
  158. package/src/lib/tables/context-builder.ts +139 -0
  159. package/src/lib/tables/formula-engine.ts +415 -0
  160. package/src/lib/tables/history.ts +115 -0
  161. package/src/lib/tables/import.ts +343 -0
  162. package/src/lib/tables/query-builder.ts +152 -0
  163. package/src/lib/tables/trigger-evaluator.ts +146 -0
  164. package/src/lib/tables/types.ts +141 -0
  165. package/src/lib/tables/validation.ts +119 -0
  166. package/src/lib/utils/app-root.ts +20 -0
  167. package/src/lib/utils/stagent-paths.ts +20 -0
  168. package/src/lib/validators/__tests__/task.test.ts +43 -10
  169. package/src/lib/validators/task.ts +7 -1
  170. package/src/lib/workflows/blueprints/registry.ts +3 -3
  171. package/src/lib/workflows/engine.ts +24 -8
  172. package/src/lib/workflows/types.ts +14 -0
  173. package/tsconfig.json +3 -1
  174. package/public/icon.svg +0 -13
  175. package/src/components/tasks/file-upload.tsx +0 -120
  176. /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 { workflows, tasks, agentLogs, notifications, documents } from "@/lib/db/schema";
5
- import { eq, and, desc } from "drizzle-orm";
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(
@@ -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(3);
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(3);
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("messaging");
55
- expect(profiles[2].config.domain).toBe("personal");
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(3);
62
- expect(createProfile).toHaveBeenCalledTimes(3);
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(3);
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("Revenue Operations Analyst")
79
+ expect.stringContaining("GTM Launch Strategist")
80
80
  );
81
- expect(createProfile).toHaveBeenCalledTimes(2);
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[2]
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[2]);
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-pipeline",
115
- "project-trip",
116
- "project-tax",
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(4);
122
- expect(schedules.map((schedule) => schedule.projectId)).toEqual([
123
- projectIds[0],
124
- projectIds[1],
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
- expect(schedules.some((schedule) => schedule.agentProfile === "project-manager")).toBe(true);
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
  });