stagent 0.9.5 → 0.10.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 (277) hide show
  1. package/README.md +5 -42
  2. package/dist/cli.js +42 -18
  3. package/docs/.coverage-gaps.json +13 -55
  4. package/docs/.last-generated +1 -1
  5. package/docs/features/provider-runtimes.md +4 -0
  6. package/docs/features/schedules.md +32 -4
  7. package/docs/features/settings.md +28 -5
  8. package/docs/features/tables.md +9 -2
  9. package/docs/features/workflows.md +10 -4
  10. package/docs/journeys/developer.md +15 -1
  11. package/docs/journeys/personal-use.md +21 -4
  12. package/docs/superpowers/plans/2026-04-07-instance-bootstrap.md +1691 -0
  13. package/docs/superpowers/plans/2026-04-08-schedule-orchestration.md +2983 -0
  14. package/docs/superpowers/plans/2026-04-11-schedule-maxturns-api-control.md +551 -0
  15. package/docs/superpowers/plans/2026-04-11-task-create-profile-validation.md +864 -0
  16. package/docs/superpowers/plans/2026-04-11-task-runtime-stagent-mcp-injection.md +739 -0
  17. package/docs/superpowers/specs/2026-04-08-chat-sse-resilience-hotfix-design.md +201 -0
  18. package/docs/superpowers/specs/2026-04-08-schedule-orchestration-design.md +371 -0
  19. package/docs/superpowers/specs/2026-04-08-swarm-visibility-design.md +213 -0
  20. package/package.json +3 -2
  21. package/src/__tests__/instrumentation-smoke.test.ts +15 -0
  22. package/src/app/analytics/page.tsx +1 -21
  23. package/src/app/api/chat/conversations/[id]/messages/route.ts +22 -1
  24. package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
  25. package/src/app/api/instance/config/route.ts +41 -0
  26. package/src/app/api/instance/init/route.ts +34 -0
  27. package/src/app/api/instance/upgrade/check/route.ts +26 -0
  28. package/src/app/api/instance/upgrade/route.ts +96 -0
  29. package/src/app/api/instance/upgrade/status/route.ts +35 -0
  30. package/src/app/api/memory/route.ts +0 -11
  31. package/src/app/api/notifications/route.ts +4 -2
  32. package/src/app/api/projects/[id]/route.ts +5 -155
  33. package/src/app/api/projects/__tests__/delete-project.test.ts +10 -19
  34. package/src/app/api/schedules/[id]/execute/route.ts +111 -0
  35. package/src/app/api/schedules/[id]/route.ts +9 -1
  36. package/src/app/api/schedules/__tests__/execute-route.test.ts +118 -0
  37. package/src/app/api/schedules/route.ts +3 -12
  38. package/src/app/api/settings/openai/login/route.ts +22 -0
  39. package/src/app/api/settings/openai/logout/route.ts +7 -0
  40. package/src/app/api/settings/openai/route.ts +21 -1
  41. package/src/app/api/settings/providers/route.ts +35 -8
  42. package/src/app/api/tables/[id]/enrich/__tests__/route.test.ts +153 -0
  43. package/src/app/api/tables/[id]/enrich/plan/route.ts +98 -0
  44. package/src/app/api/tables/[id]/enrich/route.ts +147 -0
  45. package/src/app/api/tables/[id]/enrich/runs/route.ts +25 -0
  46. package/src/app/api/tasks/[id]/execute/route.ts +0 -21
  47. package/src/app/api/workflows/[id]/resume/route.ts +59 -0
  48. package/src/app/api/workflows/[id]/status/route.ts +22 -8
  49. package/src/app/api/workspace/context/route.ts +2 -0
  50. package/src/app/api/workspace/fix-data-dir/route.ts +81 -0
  51. package/src/app/chat/page.tsx +11 -0
  52. package/src/app/inbox/page.tsx +12 -5
  53. package/src/app/layout.tsx +42 -21
  54. package/src/app/page.tsx +0 -2
  55. package/src/app/settings/page.tsx +6 -9
  56. package/src/components/chat/__tests__/chat-session-provider.test.tsx +408 -0
  57. package/src/components/chat/chat-command-popover.tsx +2 -2
  58. package/src/components/chat/chat-input.tsx +2 -3
  59. package/src/components/chat/chat-session-provider.tsx +720 -0
  60. package/src/components/chat/chat-shell.tsx +92 -401
  61. package/src/components/instance/__tests__/instance-section.test.tsx +125 -0
  62. package/src/components/instance/instance-section.tsx +382 -0
  63. package/src/components/instance/upgrade-badge.tsx +219 -0
  64. package/src/components/notifications/__tests__/batch-proposal-review.test.tsx +95 -0
  65. package/src/components/notifications/__tests__/notification-item.test.tsx +106 -0
  66. package/src/components/notifications/batch-proposal-review.tsx +20 -5
  67. package/src/components/notifications/inbox-list.tsx +11 -2
  68. package/src/components/notifications/notification-item.tsx +56 -2
  69. package/src/components/notifications/pending-approval-host.tsx +56 -37
  70. package/src/components/schedules/schedule-create-sheet.tsx +19 -1
  71. package/src/components/schedules/schedule-edit-sheet.tsx +20 -1
  72. package/src/components/schedules/schedule-form.tsx +31 -0
  73. package/src/components/settings/__tests__/providers-runtimes-section.test.tsx +149 -0
  74. package/src/components/settings/auth-method-selector.tsx +19 -4
  75. package/src/components/settings/auth-status-badge.tsx +28 -3
  76. package/src/components/settings/openai-chatgpt-auth-control.tsx +278 -0
  77. package/src/components/settings/openai-runtime-section.tsx +7 -1
  78. package/src/components/settings/providers-runtimes-section.tsx +138 -19
  79. package/src/components/shared/app-sidebar.tsx +4 -3
  80. package/src/components/shared/command-palette.tsx +4 -5
  81. package/src/components/shared/theme-toggle.tsx +5 -24
  82. package/src/components/shared/workspace-indicator.tsx +61 -2
  83. package/src/components/tables/__tests__/table-enrichment-sheet.test.tsx +130 -0
  84. package/src/components/tables/table-create-sheet.tsx +4 -0
  85. package/src/components/tables/table-enrichment-runs.tsx +103 -0
  86. package/src/components/tables/table-enrichment-sheet.tsx +538 -0
  87. package/src/components/tables/table-spreadsheet.tsx +29 -5
  88. package/src/components/tables/table-toolbar.tsx +10 -1
  89. package/src/components/tasks/kanban-board.tsx +1 -0
  90. package/src/components/tasks/kanban-column.tsx +53 -14
  91. package/src/components/tasks/task-bento-grid.tsx +19 -0
  92. package/src/components/tasks/task-card.tsx +26 -3
  93. package/src/components/tasks/task-chip-bar.tsx +24 -0
  94. package/src/components/tasks/task-result-renderer.tsx +1 -1
  95. package/src/components/workflows/delay-step-body.tsx +109 -0
  96. package/src/components/workflows/hooks/use-workflow-status.ts +50 -0
  97. package/src/components/workflows/loop-status-view.tsx +1 -1
  98. package/src/components/workflows/shared/step-result.tsx +78 -0
  99. package/src/components/workflows/shared/workflow-header.tsx +141 -0
  100. package/src/components/workflows/shared/workflow-loading-skeleton.tsx +36 -0
  101. package/src/components/workflows/swarm-dashboard.tsx +2 -15
  102. package/src/components/workflows/views/loop-pattern-view.tsx +137 -0
  103. package/src/components/workflows/views/sequence-pattern-view.tsx +511 -0
  104. package/src/components/workflows/workflow-form-view.tsx +133 -16
  105. package/src/components/workflows/workflow-status-view.tsx +30 -740
  106. package/src/instrumentation-node.ts +94 -0
  107. package/src/instrumentation.ts +4 -48
  108. package/src/lib/agents/__tests__/claude-agent.test.ts +199 -0
  109. package/src/lib/agents/__tests__/execution-manager.test.ts +1 -27
  110. package/src/lib/agents/__tests__/failure-reason.test.ts +68 -0
  111. package/src/lib/agents/__tests__/learned-context.test.ts +0 -11
  112. package/src/lib/agents/__tests__/learning-session.test.ts +158 -0
  113. package/src/lib/agents/__tests__/pattern-extractor.test.ts +48 -0
  114. package/src/lib/agents/claude-agent.ts +155 -18
  115. package/src/lib/agents/execution-manager.ts +0 -35
  116. package/src/lib/agents/learned-context.ts +0 -12
  117. package/src/lib/agents/learning-session.ts +18 -5
  118. package/src/lib/agents/profiles/__tests__/registry.test.ts +6 -4
  119. package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +70 -0
  120. package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +32 -0
  121. package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
  122. package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
  123. package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
  124. package/src/lib/agents/runtime/openai-codex.ts +29 -60
  125. package/src/lib/agents/runtime/types.ts +8 -0
  126. package/src/lib/book/chapter-mapping.ts +11 -0
  127. package/src/lib/book/content.ts +10 -0
  128. package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
  129. package/src/lib/chat/__tests__/finalize-safety-net.test.ts +139 -0
  130. package/src/lib/chat/__tests__/reconcile.test.ts +137 -0
  131. package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
  132. package/src/lib/chat/active-streams.ts +27 -0
  133. package/src/lib/chat/codex-engine.ts +16 -17
  134. package/src/lib/chat/context-builder.ts +5 -3
  135. package/src/lib/chat/engine.ts +50 -3
  136. package/src/lib/chat/reconcile.ts +117 -0
  137. package/src/lib/chat/stagent-tools.ts +1 -0
  138. package/src/lib/chat/stream-telemetry.ts +132 -0
  139. package/src/lib/chat/suggested-prompts.ts +28 -1
  140. package/src/lib/chat/system-prompt.ts +26 -1
  141. package/src/lib/chat/tool-catalog.ts +2 -1
  142. package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
  143. package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
  144. package/src/lib/chat/tools/__tests__/task-tools.test.ts +352 -0
  145. package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +217 -0
  146. package/src/lib/chat/tools/document-tools.ts +29 -13
  147. package/src/lib/chat/tools/helpers.ts +39 -0
  148. package/src/lib/chat/tools/notification-tools.ts +9 -5
  149. package/src/lib/chat/tools/project-tools.ts +33 -0
  150. package/src/lib/chat/tools/schedule-tools.ts +44 -11
  151. package/src/lib/chat/tools/table-tools.ts +71 -0
  152. package/src/lib/chat/tools/task-tools.ts +84 -20
  153. package/src/lib/chat/tools/workflow-tools.ts +234 -32
  154. package/src/lib/constants/settings.ts +8 -18
  155. package/src/lib/data/__tests__/clear.test.ts +56 -2
  156. package/src/lib/data/clear.ts +20 -15
  157. package/src/lib/data/delete-project.ts +171 -0
  158. package/src/lib/db/__tests__/bootstrap.test.ts +1 -1
  159. package/src/lib/db/bootstrap.ts +45 -16
  160. package/src/lib/db/index.ts +5 -0
  161. package/src/lib/db/migrations/0009_add_app_instances.sql +25 -0
  162. package/src/lib/db/migrations/0024_add_workflow_resume_at.sql +10 -0
  163. package/src/lib/db/migrations/0025_drop_app_instances.sql +3 -0
  164. package/src/lib/db/migrations/0026_drop_license.sql +3 -0
  165. package/src/lib/db/migrations/meta/_journal.json +21 -0
  166. package/src/lib/db/schema.ts +68 -23
  167. package/src/lib/environment/workspace-context.ts +13 -1
  168. package/src/lib/import/dedup.ts +4 -54
  169. package/src/lib/instance/__tests__/bootstrap.test.ts +362 -0
  170. package/src/lib/instance/__tests__/detect.test.ts +115 -0
  171. package/src/lib/instance/__tests__/fingerprint.test.ts +48 -0
  172. package/src/lib/instance/__tests__/git-ops.test.ts +95 -0
  173. package/src/lib/instance/__tests__/settings.test.ts +83 -0
  174. package/src/lib/instance/__tests__/upgrade-poller.test.ts +131 -0
  175. package/src/lib/instance/bootstrap.ts +270 -0
  176. package/src/lib/instance/detect.ts +49 -0
  177. package/src/lib/instance/fingerprint.ts +78 -0
  178. package/src/lib/instance/git-ops.ts +95 -0
  179. package/src/lib/instance/settings.ts +61 -0
  180. package/src/lib/instance/types.ts +77 -0
  181. package/src/lib/instance/upgrade-poller.ts +153 -0
  182. package/src/lib/notifications/__tests__/visibility.test.ts +51 -0
  183. package/src/lib/notifications/visibility.ts +33 -0
  184. package/src/lib/schedules/__tests__/collision-check.test.ts +93 -0
  185. package/src/lib/schedules/__tests__/config.test.ts +62 -0
  186. package/src/lib/schedules/__tests__/firing-metrics.test.ts +99 -0
  187. package/src/lib/schedules/__tests__/integration.test.ts +82 -0
  188. package/src/lib/schedules/__tests__/slot-claim.test.ts +242 -0
  189. package/src/lib/schedules/__tests__/tick-scheduler.test.ts +102 -0
  190. package/src/lib/schedules/__tests__/turn-budget.test.ts +228 -0
  191. package/src/lib/schedules/collision-check.ts +105 -0
  192. package/src/lib/schedules/config.ts +53 -0
  193. package/src/lib/schedules/scheduler.ts +232 -13
  194. package/src/lib/schedules/slot-claim.ts +105 -0
  195. package/src/lib/settings/__tests__/openai-auth.test.ts +101 -0
  196. package/src/lib/settings/__tests__/openai-login-manager.test.ts +64 -0
  197. package/src/lib/settings/__tests__/runtime-setup.test.ts +33 -0
  198. package/src/lib/settings/openai-auth.ts +105 -10
  199. package/src/lib/settings/openai-login-manager.ts +260 -0
  200. package/src/lib/settings/runtime-setup.ts +14 -4
  201. package/src/lib/tables/__tests__/enrichment-planner.test.ts +124 -0
  202. package/src/lib/tables/__tests__/enrichment.test.ts +147 -0
  203. package/src/lib/tables/enrichment-planner.ts +454 -0
  204. package/src/lib/tables/enrichment.ts +328 -0
  205. package/src/lib/tables/query-builder.ts +5 -2
  206. package/src/lib/tables/trigger-evaluator.ts +3 -2
  207. package/src/lib/theme.ts +71 -0
  208. package/src/lib/usage/ledger.ts +2 -18
  209. package/src/lib/util/__tests__/similarity.test.ts +106 -0
  210. package/src/lib/util/similarity.ts +77 -0
  211. package/src/lib/utils/format-timestamp.ts +24 -0
  212. package/src/lib/utils/stagent-paths.ts +12 -0
  213. package/src/lib/validators/__tests__/blueprint.test.ts +172 -0
  214. package/src/lib/validators/__tests__/settings.test.ts +10 -0
  215. package/src/lib/validators/blueprint.ts +70 -9
  216. package/src/lib/validators/profile.ts +2 -2
  217. package/src/lib/validators/settings.ts +3 -1
  218. package/src/lib/workflows/__tests__/delay.test.ts +196 -0
  219. package/src/lib/workflows/__tests__/engine.test.ts +8 -0
  220. package/src/lib/workflows/__tests__/loop-executor.test.ts +54 -0
  221. package/src/lib/workflows/__tests__/post-action.test.ts +108 -0
  222. package/src/lib/workflows/blueprints/instantiator.ts +22 -1
  223. package/src/lib/workflows/blueprints/types.ts +10 -2
  224. package/src/lib/workflows/delay.ts +106 -0
  225. package/src/lib/workflows/engine.ts +207 -4
  226. package/src/lib/workflows/loop-executor.ts +349 -24
  227. package/src/lib/workflows/post-action.ts +91 -0
  228. package/src/lib/workflows/types.ts +166 -1
  229. package/src/app/api/license/checkout/route.ts +0 -28
  230. package/src/app/api/license/portal/route.ts +0 -26
  231. package/src/app/api/license/route.ts +0 -89
  232. package/src/app/api/license/usage/route.ts +0 -63
  233. package/src/app/api/marketplace/browse/route.ts +0 -15
  234. package/src/app/api/marketplace/import/route.ts +0 -28
  235. package/src/app/api/marketplace/publish/route.ts +0 -40
  236. package/src/app/api/onboarding/email/route.ts +0 -53
  237. package/src/app/api/settings/telemetry/route.ts +0 -14
  238. package/src/app/api/sync/export/route.ts +0 -54
  239. package/src/app/api/sync/restore/route.ts +0 -37
  240. package/src/app/api/sync/sessions/route.ts +0 -24
  241. package/src/app/auth/callback/route.ts +0 -73
  242. package/src/app/marketplace/page.tsx +0 -19
  243. package/src/components/analytics/analytics-gate-card.tsx +0 -101
  244. package/src/components/marketplace/blueprint-card.tsx +0 -61
  245. package/src/components/marketplace/marketplace-browser.tsx +0 -131
  246. package/src/components/onboarding/email-capture-card.tsx +0 -104
  247. package/src/components/settings/activation-form.tsx +0 -95
  248. package/src/components/settings/cloud-account-section.tsx +0 -147
  249. package/src/components/settings/cloud-sync-section.tsx +0 -155
  250. package/src/components/settings/subscription-section.tsx +0 -410
  251. package/src/components/settings/telemetry-section.tsx +0 -80
  252. package/src/components/shared/premium-gate-overlay.tsx +0 -50
  253. package/src/components/shared/schedule-gate-dialog.tsx +0 -64
  254. package/src/components/shared/upgrade-banner.tsx +0 -112
  255. package/src/hooks/use-supabase-auth.ts +0 -79
  256. package/src/lib/billing/email.ts +0 -54
  257. package/src/lib/billing/products.ts +0 -80
  258. package/src/lib/billing/stripe.ts +0 -101
  259. package/src/lib/cloud/supabase-browser.ts +0 -32
  260. package/src/lib/cloud/supabase-client.ts +0 -56
  261. package/src/lib/license/__tests__/features.test.ts +0 -56
  262. package/src/lib/license/__tests__/key-format.test.ts +0 -88
  263. package/src/lib/license/__tests__/manager.test.ts +0 -64
  264. package/src/lib/license/__tests__/tier-limits.test.ts +0 -79
  265. package/src/lib/license/cloud-validation.ts +0 -60
  266. package/src/lib/license/features.ts +0 -44
  267. package/src/lib/license/key-format.ts +0 -101
  268. package/src/lib/license/limit-check.ts +0 -111
  269. package/src/lib/license/limit-queries.ts +0 -51
  270. package/src/lib/license/manager.ts +0 -345
  271. package/src/lib/license/notifications.ts +0 -59
  272. package/src/lib/license/tier-limits.ts +0 -71
  273. package/src/lib/marketplace/marketplace-client.ts +0 -107
  274. package/src/lib/sync/cloud-sync.ts +0 -235
  275. package/src/lib/telemetry/conversion-events.ts +0 -71
  276. package/src/lib/telemetry/queue.ts +0 -122
  277. package/src/lib/validators/license.ts +0 -33
@@ -2,8 +2,12 @@ export const SETTINGS_KEYS = {
2
2
  AUTH_METHOD: "auth.method",
3
3
  AUTH_API_KEY: "auth.apiKey",
4
4
  AUTH_API_KEY_SOURCE: "auth.apiKeySource",
5
+ OPENAI_AUTH_METHOD: "openai.authMethod",
5
6
  OPENAI_AUTH_API_KEY: "openai.authApiKey",
6
7
  OPENAI_AUTH_API_KEY_SOURCE: "openai.authApiKeySource",
8
+ OPENAI_AUTH_OAUTH_CONNECTED: "openai.oauthConnected",
9
+ OPENAI_AUTH_ACCOUNT: "openai.account",
10
+ OPENAI_AUTH_RATE_LIMITS: "openai.rateLimits",
7
11
  PERMISSIONS_ALLOW: "permissions.allow",
8
12
  BUDGET_POLICY: "usage.budgetPolicy",
9
13
  BUDGET_WARNING_STATE: "usage.budgetWarningState",
@@ -19,24 +23,10 @@ export const SETTINGS_KEYS = {
19
23
  ROUTING_PREFERENCE: "routing.preference",
20
24
  OLLAMA_BASE_URL: "ollama.baseUrl",
21
25
  OLLAMA_DEFAULT_MODEL: "ollama.defaultModel",
22
- // License / PLG
23
- LICENSE_TIER: "license.tier",
24
- LICENSE_EMAIL: "license.email",
25
- LICENSE_ACTIVATED_AT: "license.activatedAt",
26
- LICENSE_EXPIRES_AT: "license.expiresAt",
27
- LICENSE_GRACE_UNTIL: "license.graceUntil",
28
- // Supabase cloud
29
- SUPABASE_URL: "cloud.supabaseUrl",
30
- SUPABASE_ANON_KEY: "cloud.supabaseAnonKey",
31
- // Telemetry (opt-in)
32
- TELEMETRY_ENABLED: "telemetry.enabled",
33
- TELEMETRY_RUNTIME_ID: "telemetry.runtimeId",
34
- TELEMETRY_BATCH: "telemetry.batch",
35
- // Cloud sync
36
- DEVICE_ID: "sync.deviceId",
37
- LAST_SYNC_AT: "sync.lastSyncAt",
38
- // Stripe
39
- STRIPE_CUSTOMER_ID: "billing.stripeCustomerId",
26
+ // Schedule orchestration
27
+ SCHEDULE_MAX_CONCURRENT: "schedule.maxConcurrent",
28
+ SCHEDULE_MAX_RUN_DURATION_SEC: "schedule.maxRunDurationSec",
29
+ SCHEDULE_CHAT_PRESSURE_DELAY_SEC: "schedule.chatPressureDelaySec",
40
30
  } as const;
41
31
 
42
32
  export type RoutingPreference = "cost" | "latency" | "quality" | "manual";
@@ -2,10 +2,16 @@ import { describe, expect, it } from "vitest";
2
2
  import { readFileSync } from "fs";
3
3
  import { join } from "path";
4
4
  import * as schema from "@/lib/db/schema";
5
+ import { db } from "@/lib/db";
6
+ import { conversations, documents } from "@/lib/db/schema";
7
+ import { clearAllData } from "../clear";
5
8
 
6
9
  /**
7
10
  * Safety-net test: every table exported from schema.ts must appear in clear.ts
8
- * (except `settings`, which is intentionally preserved across clears).
11
+ * (except tables in INTENTIONALLY_PRESERVED, which are kept across clears:
12
+ * - settings: auth config
13
+ * - snapshots: backups, not working data
14
+ * - license: paid tier activation — clearing data must not silently downgrade)
9
15
  *
10
16
  * When you add a new table to schema.ts, this test will fail until you add a
11
17
  * corresponding db.delete() call to clear.ts in the correct FK-safe order.
@@ -13,7 +19,7 @@ import * as schema from "@/lib/db/schema";
13
19
  describe("clearAllData coverage", () => {
14
20
  const INTENTIONALLY_PRESERVED = ["settings", "snapshots"];
15
21
 
16
- it("deletes every schema table (except settings)", () => {
22
+ it("deletes every schema table (except preserved ones)", () => {
17
23
  const clearSource = readFileSync(
18
24
  join(__dirname, "..", "clear.ts"),
19
25
  "utf-8"
@@ -40,3 +46,51 @@ describe("clearAllData coverage", () => {
40
46
  expect(missing, `Tables missing from clear.ts: ${missing.join(", ")}`).toEqual([]);
41
47
  });
42
48
  });
49
+
50
+ /**
51
+ * FK ordering regression: `documents.conversation_id` references `conversations.id`.
52
+ * If clearAllData deletes `conversations` before `documents`, SQLite raises
53
+ * FOREIGN KEY constraint failed. This test seeds a document attached to a
54
+ * conversation and then calls clearAllData to ensure the ordering holds.
55
+ *
56
+ * Incident: the stagent-growth domain clone (2026-04-07) hit this because its
57
+ * seeded data included chat-attached documents.
58
+ */
59
+ describe("clearAllData FK ordering", () => {
60
+ it("clears a conversation that has an attached document without FK violation", () => {
61
+ const now = new Date();
62
+ const conversationId = "test-conv-fk-ordering";
63
+ const documentId = "test-doc-fk-ordering";
64
+
65
+ db.insert(conversations)
66
+ .values({
67
+ id: conversationId,
68
+ runtimeId: "test-runtime",
69
+ status: "active",
70
+ createdAt: now,
71
+ updatedAt: now,
72
+ })
73
+ .run();
74
+
75
+ db.insert(documents)
76
+ .values({
77
+ id: documentId,
78
+ filename: "fk-ordering-test.txt",
79
+ originalName: "fk-ordering-test.txt",
80
+ mimeType: "text/plain",
81
+ size: 10,
82
+ storagePath: "/tmp/fk-ordering-test.txt",
83
+ conversationId,
84
+ createdAt: now,
85
+ updatedAt: now,
86
+ })
87
+ .run();
88
+
89
+ expect(() => clearAllData()).not.toThrow();
90
+
91
+ const remainingConvs = db.select().from(conversations).all();
92
+ const remainingDocs = db.select().from(documents).all();
93
+ expect(remainingConvs).toHaveLength(0);
94
+ expect(remainingDocs).toHaveLength(0);
95
+ });
96
+ });
@@ -25,7 +25,6 @@ import {
25
25
  channelBindings,
26
26
  channelConfigs,
27
27
  agentMessages,
28
- license,
29
28
  workflowDocumentInputs,
30
29
  scheduleDocumentInputs,
31
30
  projectDocumentDefaults,
@@ -43,6 +42,7 @@ import {
43
42
  workflowTableInputs,
44
43
  scheduleTableInputs,
45
44
  workflowExecutionStats,
45
+ scheduleFiringMetrics,
46
46
  } from "@/lib/db/schema";
47
47
  import { readdirSync, unlinkSync, mkdirSync } from "fs";
48
48
  import { join } from "path";
@@ -55,7 +55,9 @@ const screenshotsDir = join(dataDir, "screenshots");
55
55
 
56
56
  /**
57
57
  * Wipe all data tables (FK-safe order) and uploaded files.
58
- * Preserves the settings table (auth config).
58
+ * Preserves the settings table (auth config) and the license table
59
+ * (paid tier activation) — clearing operational data should never
60
+ * silently downgrade a paid instance back to community.
59
61
  */
60
62
  export function clearAllData() {
61
63
  const sampleProfilesDeleted = clearSampleProfiles();
@@ -68,7 +70,18 @@ export function clearAllData() {
68
70
  const envScansDeleted = db.delete(environmentScans).run().changes;
69
71
  const envTemplatesDeleted = db.delete(environmentTemplates).run().changes;
70
72
 
71
- // Chat tables (messages before conversations FK safety)
73
+ // Document junction tables delete before documents (they reference documents).
74
+ // Also referenced by projects/workflows/schedules, deleted later.
75
+ const workflowDocInputsDeleted = db.delete(workflowDocumentInputs).run().changes;
76
+ const scheduleDocInputsDeleted = db.delete(scheduleDocumentInputs).run().changes;
77
+ const projectDocDefaultsDeleted = db.delete(projectDocumentDefaults).run().changes;
78
+ // Documents reference conversations (documents.conversation_id) — must delete
79
+ // before conversations to avoid FK violation when chat-attached documents exist.
80
+ const documentsDeleted = db.delete(documents).run().changes;
81
+
82
+ // Chat tables: channel_bindings + chat_messages + documents all reference
83
+ // conversations — delete them before conversations.
84
+ const channelBindingsDeleted = db.delete(channelBindings).run().changes;
72
85
  const chatMessagesDeleted = db.delete(chatMessages).run().changes;
73
86
  const conversationsDeleted = db.delete(conversations).run().changes;
74
87
 
@@ -76,15 +89,12 @@ export function clearAllData() {
76
89
  const bookmarksDeleted = db.delete(bookmarks).run().changes;
77
90
  const readingProgressDeleted = db.delete(readingProgress).run().changes;
78
91
 
79
- // Channel bindings reference channel_configs + conversations — delete before both
80
- const channelBindingsDeleted = db.delete(channelBindings).run().changes;
81
-
82
92
  // Agent messages reference tasks — delete before tasks
83
93
  const agentMessagesDeleted = db.delete(agentMessages).run().changes;
84
94
  const channelConfigsDeleted = db.delete(channelConfigs).run().changes;
85
95
 
86
- // License table — no FK dependencies
87
- const licenseDeleted = db.delete(license).run().changes;
96
+ // License table is intentionally preserved clearing operational data
97
+ // should never downgrade a paid instance back to community tier.
88
98
 
89
99
  // Snapshots are intentionally preserved — they are backups, not working data
90
100
 
@@ -112,17 +122,12 @@ export function clearAllData() {
112
122
  const userTablesDeleted = db.delete(userTables).run().changes;
113
123
  const userTableTemplatesDeleted = db.delete(userTableTemplates).run().changes;
114
124
 
115
- // Document junction tables — delete before documents, workflows, schedules, projects
116
- const workflowDocInputsDeleted = db.delete(workflowDocumentInputs).run().changes;
117
- const scheduleDocInputsDeleted = db.delete(scheduleDocumentInputs).run().changes;
118
- const projectDocDefaultsDeleted = db.delete(projectDocumentDefaults).run().changes;
119
-
120
- const documentsDeleted = db.delete(documents).run().changes;
121
125
  const agentMemoryDeleted = db.delete(agentMemory).run().changes;
122
126
  const learnedContextDeleted = db.delete(learnedContext).run().changes;
123
127
  const executionStatsDeleted = db.delete(workflowExecutionStats).run().changes;
124
128
  const tasksDeleted = db.delete(tasks).run().changes;
125
129
  const workflowsDeleted = db.delete(workflows).run().changes;
130
+ const scheduleFiringMetricsDeleted = db.delete(scheduleFiringMetrics).run().changes;
126
131
  const schedulesDeleted = db.delete(schedules).run().changes;
127
132
  const projectsDeleted = db.delete(projects).run().changes;
128
133
 
@@ -156,6 +161,7 @@ export function clearAllData() {
156
161
  projects: projectsDeleted,
157
162
  tasks: tasksDeleted,
158
163
  workflows: workflowsDeleted,
164
+ scheduleFiringMetrics: scheduleFiringMetricsDeleted,
159
165
  schedules: schedulesDeleted,
160
166
  usageLedger: usageLedgerDeleted,
161
167
  agentLogs: logsDeleted,
@@ -195,7 +201,6 @@ export function clearAllData() {
195
201
  scheduleTableInputs: scheduleTableInputsDeleted,
196
202
  files: filesDeleted,
197
203
  screenshots: screenshotsDeleted,
198
- license: licenseDeleted,
199
204
  workflowExecutionStats: executionStatsDeleted,
200
205
  };
201
206
  }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * FK-safe cascade delete for a project and all its children.
3
+ * Shared by the API DELETE route and the delete_project chat tool.
4
+ */
5
+
6
+ import { db } from "@/lib/db";
7
+ import {
8
+ projects,
9
+ tasks,
10
+ workflows,
11
+ documents,
12
+ schedules,
13
+ agentLogs,
14
+ notifications,
15
+ learnedContext,
16
+ usageLedger,
17
+ environmentSyncOps,
18
+ environmentCheckpoints,
19
+ environmentArtifacts,
20
+ environmentScans,
21
+ chatMessages,
22
+ conversations,
23
+ projectDocumentDefaults,
24
+ userTables,
25
+ userTableColumns,
26
+ userTableRows,
27
+ userTableViews,
28
+ userTableImports,
29
+ userTableRelationships,
30
+ tableDocumentInputs,
31
+ taskTableInputs,
32
+ workflowTableInputs,
33
+ scheduleTableInputs,
34
+ userTableTriggers,
35
+ userTableRowHistory,
36
+ } from "@/lib/db/schema";
37
+ import { eq, inArray } from "drizzle-orm";
38
+
39
+ /**
40
+ * Delete a project and all FK-dependent children in safe order.
41
+ * Returns true if the project existed and was deleted, false if not found.
42
+ */
43
+ export function deleteProjectCascade(projectId: string): boolean {
44
+ const existing = db
45
+ .select({ id: projects.id })
46
+ .from(projects)
47
+ .where(eq(projects.id, projectId))
48
+ .get();
49
+
50
+ if (!existing) return false;
51
+
52
+ // 1. Collect child IDs for nested FK chains
53
+ const taskIds = db
54
+ .select({ id: tasks.id })
55
+ .from(tasks)
56
+ .where(eq(tasks.projectId, projectId))
57
+ .all()
58
+ .map((r) => r.id);
59
+
60
+ const workflowIds = db
61
+ .select({ id: workflows.id })
62
+ .from(workflows)
63
+ .where(eq(workflows.projectId, projectId))
64
+ .all()
65
+ .map((r) => r.id);
66
+
67
+ const conversationIds = db
68
+ .select({ id: conversations.id })
69
+ .from(conversations)
70
+ .where(eq(conversations.projectId, projectId))
71
+ .all()
72
+ .map((r) => r.id);
73
+
74
+ const scanIds = db
75
+ .select({ id: environmentScans.id })
76
+ .from(environmentScans)
77
+ .where(eq(environmentScans.projectId, projectId))
78
+ .all()
79
+ .map((r) => r.id);
80
+
81
+ const checkpointIds = db
82
+ .select({ id: environmentCheckpoints.id })
83
+ .from(environmentCheckpoints)
84
+ .where(eq(environmentCheckpoints.projectId, projectId))
85
+ .all()
86
+ .map((r) => r.id);
87
+
88
+ // 2. Environment tables (deepest children first)
89
+ if (checkpointIds.length > 0) {
90
+ db.delete(environmentSyncOps)
91
+ .where(inArray(environmentSyncOps.checkpointId, checkpointIds))
92
+ .run();
93
+ db.delete(environmentCheckpoints)
94
+ .where(inArray(environmentCheckpoints.id, checkpointIds))
95
+ .run();
96
+ }
97
+ if (scanIds.length > 0) {
98
+ db.delete(environmentArtifacts)
99
+ .where(inArray(environmentArtifacts.scanId, scanIds))
100
+ .run();
101
+ db.delete(environmentScans)
102
+ .where(inArray(environmentScans.id, scanIds))
103
+ .run();
104
+ }
105
+
106
+ // 3. Chat tables (messages before conversations)
107
+ if (conversationIds.length > 0) {
108
+ db.delete(chatMessages)
109
+ .where(inArray(chatMessages.conversationId, conversationIds))
110
+ .run();
111
+ db.delete(conversations)
112
+ .where(inArray(conversations.id, conversationIds))
113
+ .run();
114
+ }
115
+
116
+ // 4. Usage ledger
117
+ db.delete(usageLedger).where(eq(usageLedger.projectId, projectId)).run();
118
+
119
+ // 5. Task children (logs, notifications, documents, learned context)
120
+ if (taskIds.length > 0) {
121
+ db.delete(agentLogs).where(inArray(agentLogs.taskId, taskIds)).run();
122
+ db.delete(notifications)
123
+ .where(inArray(notifications.taskId, taskIds))
124
+ .run();
125
+ db.delete(documents).where(inArray(documents.taskId, taskIds)).run();
126
+ db.delete(learnedContext)
127
+ .where(inArray(learnedContext.sourceTaskId, taskIds))
128
+ .run();
129
+ }
130
+
131
+ // 6. Junction tables
132
+ db.delete(projectDocumentDefaults).where(eq(projectDocumentDefaults.projectId, projectId)).run();
133
+
134
+ // 6b. User-defined tables — cascade-delete children before parent
135
+ const tableIds = db
136
+ .select({ id: userTables.id })
137
+ .from(userTables)
138
+ .where(eq(userTables.projectId, projectId))
139
+ .all()
140
+ .map((r) => r.id);
141
+
142
+ if (tableIds.length > 0) {
143
+ // Junction tables first
144
+ db.delete(tableDocumentInputs).where(inArray(tableDocumentInputs.tableId, tableIds)).run();
145
+ db.delete(taskTableInputs).where(inArray(taskTableInputs.tableId, tableIds)).run();
146
+ db.delete(workflowTableInputs).where(inArray(workflowTableInputs.tableId, tableIds)).run();
147
+ db.delete(scheduleTableInputs).where(inArray(scheduleTableInputs.tableId, tableIds)).run();
148
+ // Children
149
+ db.delete(userTableRowHistory).where(inArray(userTableRowHistory.tableId, tableIds)).run();
150
+ db.delete(userTableTriggers).where(inArray(userTableTriggers.tableId, tableIds)).run();
151
+ db.delete(userTableImports).where(inArray(userTableImports.tableId, tableIds)).run();
152
+ db.delete(userTableViews).where(inArray(userTableViews.tableId, tableIds)).run();
153
+ db.delete(userTableRelationships).where(inArray(userTableRelationships.fromTableId, tableIds)).run();
154
+ db.delete(userTableRows).where(inArray(userTableRows.tableId, tableIds)).run();
155
+ db.delete(userTableColumns).where(inArray(userTableColumns.tableId, tableIds)).run();
156
+ db.delete(userTables).where(inArray(userTables.id, tableIds)).run();
157
+ }
158
+
159
+ // 7. Direct project children
160
+ db.delete(documents).where(eq(documents.projectId, projectId)).run();
161
+ db.delete(tasks).where(eq(tasks.projectId, projectId)).run();
162
+ if (workflowIds.length > 0) {
163
+ db.delete(workflows).where(inArray(workflows.id, workflowIds)).run();
164
+ }
165
+ db.delete(schedules).where(eq(schedules.projectId, projectId)).run();
166
+
167
+ // 8. Finally delete the project
168
+ db.delete(projects).where(eq(projects.id, projectId)).run();
169
+
170
+ return true;
171
+ }
@@ -50,7 +50,7 @@ describe("database bootstrap recovery", () => {
50
50
  const migrationCount = migratedDb
51
51
  .prepare("SELECT COUNT(*) AS count FROM __drizzle_migrations")
52
52
  .get() as { count: number };
53
- expect(migrationCount.count).toBe(9);
53
+ expect(migrationCount.count).toBe(12);
54
54
  migratedDb.close();
55
55
  });
56
56
  });
@@ -45,8 +45,8 @@ const STAGENT_TABLES = [
45
45
  "user_table_triggers",
46
46
  "user_table_row_history",
47
47
  "snapshots",
48
- "license",
49
48
  "workflow_execution_stats",
49
+ "schedule_firing_metrics",
50
50
  ] as const;
51
51
 
52
52
  export function bootstrapStagentDatabase(sqlite: Database.Database): void {
@@ -91,6 +91,7 @@ export function bootstrapStagentDatabase(sqlite: Database.Database): void {
91
91
  status TEXT DEFAULT 'draft' NOT NULL,
92
92
  run_number INTEGER DEFAULT 0 NOT NULL,
93
93
  runtime_id TEXT,
94
+ resume_at INTEGER,
94
95
  created_at INTEGER NOT NULL,
95
96
  updated_at INTEGER NOT NULL,
96
97
  FOREIGN KEY (project_id) REFERENCES projects(id) ON UPDATE NO ACTION ON DELETE NO ACTION
@@ -192,6 +193,32 @@ export function bootstrapStagentDatabase(sqlite: Database.Database): void {
192
193
  CREATE INDEX IF NOT EXISTS idx_schedules_next_fire_at ON schedules(next_fire_at);
193
194
  CREATE INDEX IF NOT EXISTS idx_schedules_project_id ON schedules(project_id);
194
195
 
196
+ -- schedule_firing_metrics is placed here (between schedules indexes and
197
+ -- notifications indexes) to satisfy its foreign-key dependency on schedules.
198
+ -- Future tables with FK dependencies should follow the same "place after
199
+ -- parent table" discipline rather than batching all CREATE TABLE at the top.
200
+ CREATE TABLE IF NOT EXISTS schedule_firing_metrics (
201
+ id TEXT PRIMARY KEY NOT NULL,
202
+ schedule_id TEXT NOT NULL,
203
+ task_id TEXT,
204
+ fired_at INTEGER NOT NULL,
205
+ slot_claimed_at INTEGER,
206
+ completed_at INTEGER,
207
+ slot_wait_ms INTEGER,
208
+ duration_ms INTEGER,
209
+ turn_count INTEGER,
210
+ max_turns_at_firing INTEGER,
211
+ event_loop_lag_ms REAL,
212
+ peak_rss_mb INTEGER,
213
+ chat_streams_active INTEGER,
214
+ concurrent_schedules INTEGER,
215
+ failure_reason TEXT,
216
+ FOREIGN KEY (schedule_id) REFERENCES schedules(id) ON UPDATE NO ACTION ON DELETE NO ACTION,
217
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON UPDATE NO ACTION ON DELETE NO ACTION
218
+ );
219
+
220
+ CREATE INDEX IF NOT EXISTS idx_sfm_schedule_time ON schedule_firing_metrics(schedule_id, fired_at);
221
+
195
222
  CREATE INDEX IF NOT EXISTS idx_notifications_task_id ON notifications(task_id);
196
223
  CREATE INDEX IF NOT EXISTS idx_notifications_read ON notifications(read);
197
224
  CREATE INDEX IF NOT EXISTS idx_documents_task_id ON documents(task_id);
@@ -260,6 +287,7 @@ export function bootstrapStagentDatabase(sqlite: Database.Database): void {
260
287
 
261
288
  CREATE INDEX IF NOT EXISTS idx_views_surface ON views(surface);
262
289
  CREATE INDEX IF NOT EXISTS idx_views_surface_default ON views(surface, is_default);
290
+
263
291
  `);
264
292
 
265
293
  const addColumnIfMissing = (ddl: string) => {
@@ -305,6 +333,10 @@ export function bootstrapStagentDatabase(sqlite: Database.Database): void {
305
333
  addColumnIfMissing(`ALTER TABLE documents ADD COLUMN source TEXT DEFAULT 'upload';`);
306
334
  addColumnIfMissing(`ALTER TABLE documents ADD COLUMN conversation_id TEXT REFERENCES conversations(id);`);
307
335
  addColumnIfMissing(`ALTER TABLE documents ADD COLUMN message_id TEXT;`);
336
+ // Workflow step delays — resume_at for schedule-based delay resumption.
337
+ // The partial index on resume_at is created by migration 0024 for fresh DBs;
338
+ // existing DBs that don't run migrations will do a small table scan instead.
339
+ addColumnIfMissing(`ALTER TABLE workflows ADD COLUMN resume_at INTEGER;`);
308
340
  sqlite.exec(`CREATE INDEX IF NOT EXISTS idx_documents_source ON documents(source);`);
309
341
  sqlite.exec(`CREATE INDEX IF NOT EXISTS idx_documents_conversation_id ON documents(conversation_id);`);
310
342
 
@@ -552,6 +584,18 @@ export function bootstrapStagentDatabase(sqlite: Database.Database): void {
552
584
  addColumnIfMissing(`ALTER TABLE schedules ADD COLUMN last_failure_reason TEXT;`);
553
585
  addColumnIfMissing(`ALTER TABLE channel_configs ADD COLUMN direction TEXT DEFAULT 'outbound' NOT NULL;`);
554
586
 
587
+ // ── Schedule Orchestration columns ───────────────────────────────────────
588
+ addColumnIfMissing(`ALTER TABLE tasks ADD COLUMN slot_claimed_at INTEGER;`);
589
+ addColumnIfMissing(`ALTER TABLE tasks ADD COLUMN lease_expires_at INTEGER;`);
590
+ addColumnIfMissing(`ALTER TABLE tasks ADD COLUMN failure_reason TEXT;`);
591
+ addColumnIfMissing(`ALTER TABLE tasks ADD COLUMN max_turns INTEGER;`);
592
+ addColumnIfMissing(`ALTER TABLE schedules ADD COLUMN max_turns INTEGER;`);
593
+ addColumnIfMissing(`ALTER TABLE schedules ADD COLUMN max_turns_set_at INTEGER;`);
594
+ addColumnIfMissing(`ALTER TABLE schedules ADD COLUMN max_run_duration_sec INTEGER;`);
595
+ addColumnIfMissing(`ALTER TABLE schedules ADD COLUMN turn_budget_breach_streak INTEGER DEFAULT 0 NOT NULL;`);
596
+ // Create the composite index for lease-reaper queries (idempotent)
597
+ sqlite.exec(`CREATE INDEX IF NOT EXISTS idx_tasks_running_scheduled ON tasks(status, source_type, lease_expires_at);`);
598
+
555
599
  // ── Bidirectional Channel Chat ──────────────────────────────────────────
556
600
  sqlite.exec(`
557
601
  CREATE TABLE IF NOT EXISTS channel_bindings (
@@ -856,21 +900,6 @@ export function bootstrapStagentDatabase(sqlite: Database.Database): void {
856
900
  CREATE INDEX IF NOT EXISTS idx_snapshots_type ON snapshots(type);
857
901
  CREATE INDEX IF NOT EXISTS idx_snapshots_created_at ON snapshots(created_at);
858
902
 
859
- CREATE TABLE IF NOT EXISTS license (
860
- id TEXT PRIMARY KEY NOT NULL,
861
- supabase_user_id TEXT,
862
- tier TEXT DEFAULT 'community' NOT NULL,
863
- status TEXT DEFAULT 'inactive' NOT NULL,
864
- email TEXT,
865
- activated_at INTEGER,
866
- expires_at INTEGER,
867
- last_validated_at INTEGER,
868
- grace_period_expires_at INTEGER,
869
- encrypted_token TEXT,
870
- created_at INTEGER NOT NULL,
871
- updated_at INTEGER NOT NULL
872
- );
873
-
874
903
  CREATE TABLE IF NOT EXISTS workflow_execution_stats (
875
904
  id TEXT PRIMARY KEY,
876
905
  pattern TEXT NOT NULL,
@@ -13,6 +13,11 @@ const dbPath = join(dataDir, "stagent.db");
13
13
  const sqlite = new Database(dbPath);
14
14
  sqlite.pragma("journal_mode = WAL");
15
15
  sqlite.pragma("foreign_keys = ON");
16
+
17
+ // Bootstrap creates tables with IF NOT EXISTS + adds columns.
18
+ // Drizzle migrations (DROP TABLE, CREATE INDEX, etc.) run separately
19
+ // at server startup in instrumentation-node.ts to avoid SQLITE_BUSY
20
+ // conflicts during next build.
16
21
  bootstrapStagentDatabase(sqlite);
17
22
 
18
23
  export const db = drizzle(sqlite, { schema });
@@ -0,0 +1,25 @@
1
+ -- Historical migration: app_instances table (removed in 0025).
2
+ -- Kept for Drizzle journal compatibility.
3
+ CREATE TABLE IF NOT EXISTS app_instances (
4
+ id TEXT PRIMARY KEY NOT NULL,
5
+ app_id TEXT NOT NULL,
6
+ name TEXT NOT NULL,
7
+ version TEXT NOT NULL,
8
+ project_id TEXT,
9
+ manifest_json TEXT NOT NULL,
10
+ ui_schema_json TEXT NOT NULL,
11
+ resource_map_json TEXT DEFAULT '{}' NOT NULL,
12
+ status TEXT DEFAULT 'installing' NOT NULL,
13
+ source_type TEXT DEFAULT 'builtin' NOT NULL,
14
+ bootstrap_error TEXT,
15
+ installed_at INTEGER NOT NULL,
16
+ bootstrapped_at INTEGER,
17
+ updated_at INTEGER NOT NULL,
18
+ FOREIGN KEY (project_id) REFERENCES projects(id) ON UPDATE NO ACTION ON DELETE NO ACTION
19
+ );
20
+ --> statement-breakpoint
21
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_app_instances_app_id ON app_instances(app_id);
22
+ --> statement-breakpoint
23
+ CREATE INDEX IF NOT EXISTS idx_app_instances_project_id ON app_instances(project_id);
24
+ --> statement-breakpoint
25
+ CREATE INDEX IF NOT EXISTS idx_app_instances_status ON app_instances(status);
@@ -0,0 +1,10 @@
1
+ -- Workflow Step Delays — Track A.2
2
+ -- Adds resume_at timestamp column to workflows for schedule-based delay resumption.
3
+ -- When a workflow pauses at a delay step, it writes resume_at = now + delayDuration.
4
+ -- The scheduler tick queries WHERE status='paused' AND resume_at <= now() to find due workflows.
5
+ -- Partial index keeps the query cheap: only paused workflows with a pending resume are indexed.
6
+
7
+ ALTER TABLE workflows ADD COLUMN resume_at INTEGER;
8
+ CREATE INDEX IF NOT EXISTS idx_workflows_resume_at
9
+ ON workflows(resume_at)
10
+ WHERE resume_at IS NOT NULL;
@@ -0,0 +1,3 @@
1
+ -- Remove the deprecated app_instances table.
2
+ -- IF EXISTS guards against fresh databases that never had this table.
3
+ DROP TABLE IF EXISTS app_instances;
@@ -0,0 +1,3 @@
1
+ -- Remove the license table (Community Edition simplification).
2
+ -- IF EXISTS guards against fresh databases that never had this table.
3
+ DROP TABLE IF EXISTS license;
@@ -64,6 +64,27 @@
64
64
  "when": 1773705600000,
65
65
  "tag": "0008_add_document_version",
66
66
  "breakpoints": true
67
+ },
68
+ {
69
+ "idx": 9,
70
+ "version": "7",
71
+ "when": 1775779200000,
72
+ "tag": "0009_add_app_instances",
73
+ "breakpoints": true
74
+ },
75
+ {
76
+ "idx": 10,
77
+ "version": "7",
78
+ "when": 1776470400000,
79
+ "tag": "0025_drop_app_instances",
80
+ "breakpoints": true
81
+ },
82
+ {
83
+ "idx": 11,
84
+ "version": "7",
85
+ "when": 1776556800000,
86
+ "tag": "0026_drop_license",
87
+ "breakpoints": true
67
88
  }
68
89
  ]
69
90
  }