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.
- package/README.md +5 -42
- package/dist/cli.js +42 -18
- package/docs/.coverage-gaps.json +13 -55
- package/docs/.last-generated +1 -1
- package/docs/features/provider-runtimes.md +4 -0
- package/docs/features/schedules.md +32 -4
- package/docs/features/settings.md +28 -5
- package/docs/features/tables.md +9 -2
- package/docs/features/workflows.md +10 -4
- package/docs/journeys/developer.md +15 -1
- package/docs/journeys/personal-use.md +21 -4
- package/docs/superpowers/plans/2026-04-07-instance-bootstrap.md +1691 -0
- package/docs/superpowers/plans/2026-04-08-schedule-orchestration.md +2983 -0
- package/docs/superpowers/plans/2026-04-11-schedule-maxturns-api-control.md +551 -0
- package/docs/superpowers/plans/2026-04-11-task-create-profile-validation.md +864 -0
- package/docs/superpowers/plans/2026-04-11-task-runtime-stagent-mcp-injection.md +739 -0
- package/docs/superpowers/specs/2026-04-08-chat-sse-resilience-hotfix-design.md +201 -0
- package/docs/superpowers/specs/2026-04-08-schedule-orchestration-design.md +371 -0
- package/docs/superpowers/specs/2026-04-08-swarm-visibility-design.md +213 -0
- package/package.json +3 -2
- package/src/__tests__/instrumentation-smoke.test.ts +15 -0
- package/src/app/analytics/page.tsx +1 -21
- package/src/app/api/chat/conversations/[id]/messages/route.ts +22 -1
- package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
- package/src/app/api/instance/config/route.ts +41 -0
- package/src/app/api/instance/init/route.ts +34 -0
- package/src/app/api/instance/upgrade/check/route.ts +26 -0
- package/src/app/api/instance/upgrade/route.ts +96 -0
- package/src/app/api/instance/upgrade/status/route.ts +35 -0
- package/src/app/api/memory/route.ts +0 -11
- package/src/app/api/notifications/route.ts +4 -2
- package/src/app/api/projects/[id]/route.ts +5 -155
- package/src/app/api/projects/__tests__/delete-project.test.ts +10 -19
- package/src/app/api/schedules/[id]/execute/route.ts +111 -0
- package/src/app/api/schedules/[id]/route.ts +9 -1
- package/src/app/api/schedules/__tests__/execute-route.test.ts +118 -0
- package/src/app/api/schedules/route.ts +3 -12
- package/src/app/api/settings/openai/login/route.ts +22 -0
- package/src/app/api/settings/openai/logout/route.ts +7 -0
- package/src/app/api/settings/openai/route.ts +21 -1
- package/src/app/api/settings/providers/route.ts +35 -8
- package/src/app/api/tables/[id]/enrich/__tests__/route.test.ts +153 -0
- package/src/app/api/tables/[id]/enrich/plan/route.ts +98 -0
- package/src/app/api/tables/[id]/enrich/route.ts +147 -0
- package/src/app/api/tables/[id]/enrich/runs/route.ts +25 -0
- package/src/app/api/tasks/[id]/execute/route.ts +0 -21
- package/src/app/api/workflows/[id]/resume/route.ts +59 -0
- package/src/app/api/workflows/[id]/status/route.ts +22 -8
- package/src/app/api/workspace/context/route.ts +2 -0
- package/src/app/api/workspace/fix-data-dir/route.ts +81 -0
- package/src/app/chat/page.tsx +11 -0
- package/src/app/inbox/page.tsx +12 -5
- package/src/app/layout.tsx +42 -21
- package/src/app/page.tsx +0 -2
- package/src/app/settings/page.tsx +6 -9
- package/src/components/chat/__tests__/chat-session-provider.test.tsx +408 -0
- package/src/components/chat/chat-command-popover.tsx +2 -2
- package/src/components/chat/chat-input.tsx +2 -3
- package/src/components/chat/chat-session-provider.tsx +720 -0
- package/src/components/chat/chat-shell.tsx +92 -401
- package/src/components/instance/__tests__/instance-section.test.tsx +125 -0
- package/src/components/instance/instance-section.tsx +382 -0
- package/src/components/instance/upgrade-badge.tsx +219 -0
- package/src/components/notifications/__tests__/batch-proposal-review.test.tsx +95 -0
- package/src/components/notifications/__tests__/notification-item.test.tsx +106 -0
- package/src/components/notifications/batch-proposal-review.tsx +20 -5
- package/src/components/notifications/inbox-list.tsx +11 -2
- package/src/components/notifications/notification-item.tsx +56 -2
- package/src/components/notifications/pending-approval-host.tsx +56 -37
- package/src/components/schedules/schedule-create-sheet.tsx +19 -1
- package/src/components/schedules/schedule-edit-sheet.tsx +20 -1
- package/src/components/schedules/schedule-form.tsx +31 -0
- package/src/components/settings/__tests__/providers-runtimes-section.test.tsx +149 -0
- package/src/components/settings/auth-method-selector.tsx +19 -4
- package/src/components/settings/auth-status-badge.tsx +28 -3
- package/src/components/settings/openai-chatgpt-auth-control.tsx +278 -0
- package/src/components/settings/openai-runtime-section.tsx +7 -1
- package/src/components/settings/providers-runtimes-section.tsx +138 -19
- package/src/components/shared/app-sidebar.tsx +4 -3
- package/src/components/shared/command-palette.tsx +4 -5
- package/src/components/shared/theme-toggle.tsx +5 -24
- package/src/components/shared/workspace-indicator.tsx +61 -2
- package/src/components/tables/__tests__/table-enrichment-sheet.test.tsx +130 -0
- package/src/components/tables/table-create-sheet.tsx +4 -0
- package/src/components/tables/table-enrichment-runs.tsx +103 -0
- package/src/components/tables/table-enrichment-sheet.tsx +538 -0
- package/src/components/tables/table-spreadsheet.tsx +29 -5
- package/src/components/tables/table-toolbar.tsx +10 -1
- package/src/components/tasks/kanban-board.tsx +1 -0
- package/src/components/tasks/kanban-column.tsx +53 -14
- package/src/components/tasks/task-bento-grid.tsx +19 -0
- package/src/components/tasks/task-card.tsx +26 -3
- package/src/components/tasks/task-chip-bar.tsx +24 -0
- package/src/components/tasks/task-result-renderer.tsx +1 -1
- package/src/components/workflows/delay-step-body.tsx +109 -0
- package/src/components/workflows/hooks/use-workflow-status.ts +50 -0
- package/src/components/workflows/loop-status-view.tsx +1 -1
- package/src/components/workflows/shared/step-result.tsx +78 -0
- package/src/components/workflows/shared/workflow-header.tsx +141 -0
- package/src/components/workflows/shared/workflow-loading-skeleton.tsx +36 -0
- package/src/components/workflows/swarm-dashboard.tsx +2 -15
- package/src/components/workflows/views/loop-pattern-view.tsx +137 -0
- package/src/components/workflows/views/sequence-pattern-view.tsx +511 -0
- package/src/components/workflows/workflow-form-view.tsx +133 -16
- package/src/components/workflows/workflow-status-view.tsx +30 -740
- package/src/instrumentation-node.ts +94 -0
- package/src/instrumentation.ts +4 -48
- package/src/lib/agents/__tests__/claude-agent.test.ts +199 -0
- package/src/lib/agents/__tests__/execution-manager.test.ts +1 -27
- package/src/lib/agents/__tests__/failure-reason.test.ts +68 -0
- package/src/lib/agents/__tests__/learned-context.test.ts +0 -11
- package/src/lib/agents/__tests__/learning-session.test.ts +158 -0
- package/src/lib/agents/__tests__/pattern-extractor.test.ts +48 -0
- package/src/lib/agents/claude-agent.ts +155 -18
- package/src/lib/agents/execution-manager.ts +0 -35
- package/src/lib/agents/learned-context.ts +0 -12
- package/src/lib/agents/learning-session.ts +18 -5
- package/src/lib/agents/profiles/__tests__/registry.test.ts +6 -4
- package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +70 -0
- package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +32 -0
- package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
- package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
- package/src/lib/agents/runtime/openai-codex.ts +29 -60
- package/src/lib/agents/runtime/types.ts +8 -0
- package/src/lib/book/chapter-mapping.ts +11 -0
- package/src/lib/book/content.ts +10 -0
- package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
- package/src/lib/chat/__tests__/finalize-safety-net.test.ts +139 -0
- package/src/lib/chat/__tests__/reconcile.test.ts +137 -0
- package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
- package/src/lib/chat/active-streams.ts +27 -0
- package/src/lib/chat/codex-engine.ts +16 -17
- package/src/lib/chat/context-builder.ts +5 -3
- package/src/lib/chat/engine.ts +50 -3
- package/src/lib/chat/reconcile.ts +117 -0
- package/src/lib/chat/stagent-tools.ts +1 -0
- package/src/lib/chat/stream-telemetry.ts +132 -0
- package/src/lib/chat/suggested-prompts.ts +28 -1
- package/src/lib/chat/system-prompt.ts +26 -1
- package/src/lib/chat/tool-catalog.ts +2 -1
- package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
- package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
- package/src/lib/chat/tools/__tests__/task-tools.test.ts +352 -0
- package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +217 -0
- package/src/lib/chat/tools/document-tools.ts +29 -13
- package/src/lib/chat/tools/helpers.ts +39 -0
- package/src/lib/chat/tools/notification-tools.ts +9 -5
- package/src/lib/chat/tools/project-tools.ts +33 -0
- package/src/lib/chat/tools/schedule-tools.ts +44 -11
- package/src/lib/chat/tools/table-tools.ts +71 -0
- package/src/lib/chat/tools/task-tools.ts +84 -20
- package/src/lib/chat/tools/workflow-tools.ts +234 -32
- package/src/lib/constants/settings.ts +8 -18
- package/src/lib/data/__tests__/clear.test.ts +56 -2
- package/src/lib/data/clear.ts +20 -15
- package/src/lib/data/delete-project.ts +171 -0
- package/src/lib/db/__tests__/bootstrap.test.ts +1 -1
- package/src/lib/db/bootstrap.ts +45 -16
- package/src/lib/db/index.ts +5 -0
- package/src/lib/db/migrations/0009_add_app_instances.sql +25 -0
- package/src/lib/db/migrations/0024_add_workflow_resume_at.sql +10 -0
- package/src/lib/db/migrations/0025_drop_app_instances.sql +3 -0
- package/src/lib/db/migrations/0026_drop_license.sql +3 -0
- package/src/lib/db/migrations/meta/_journal.json +21 -0
- package/src/lib/db/schema.ts +68 -23
- package/src/lib/environment/workspace-context.ts +13 -1
- package/src/lib/import/dedup.ts +4 -54
- package/src/lib/instance/__tests__/bootstrap.test.ts +362 -0
- package/src/lib/instance/__tests__/detect.test.ts +115 -0
- package/src/lib/instance/__tests__/fingerprint.test.ts +48 -0
- package/src/lib/instance/__tests__/git-ops.test.ts +95 -0
- package/src/lib/instance/__tests__/settings.test.ts +83 -0
- package/src/lib/instance/__tests__/upgrade-poller.test.ts +131 -0
- package/src/lib/instance/bootstrap.ts +270 -0
- package/src/lib/instance/detect.ts +49 -0
- package/src/lib/instance/fingerprint.ts +78 -0
- package/src/lib/instance/git-ops.ts +95 -0
- package/src/lib/instance/settings.ts +61 -0
- package/src/lib/instance/types.ts +77 -0
- package/src/lib/instance/upgrade-poller.ts +153 -0
- package/src/lib/notifications/__tests__/visibility.test.ts +51 -0
- package/src/lib/notifications/visibility.ts +33 -0
- package/src/lib/schedules/__tests__/collision-check.test.ts +93 -0
- package/src/lib/schedules/__tests__/config.test.ts +62 -0
- package/src/lib/schedules/__tests__/firing-metrics.test.ts +99 -0
- package/src/lib/schedules/__tests__/integration.test.ts +82 -0
- package/src/lib/schedules/__tests__/slot-claim.test.ts +242 -0
- package/src/lib/schedules/__tests__/tick-scheduler.test.ts +102 -0
- package/src/lib/schedules/__tests__/turn-budget.test.ts +228 -0
- package/src/lib/schedules/collision-check.ts +105 -0
- package/src/lib/schedules/config.ts +53 -0
- package/src/lib/schedules/scheduler.ts +232 -13
- package/src/lib/schedules/slot-claim.ts +105 -0
- package/src/lib/settings/__tests__/openai-auth.test.ts +101 -0
- package/src/lib/settings/__tests__/openai-login-manager.test.ts +64 -0
- package/src/lib/settings/__tests__/runtime-setup.test.ts +33 -0
- package/src/lib/settings/openai-auth.ts +105 -10
- package/src/lib/settings/openai-login-manager.ts +260 -0
- package/src/lib/settings/runtime-setup.ts +14 -4
- package/src/lib/tables/__tests__/enrichment-planner.test.ts +124 -0
- package/src/lib/tables/__tests__/enrichment.test.ts +147 -0
- package/src/lib/tables/enrichment-planner.ts +454 -0
- package/src/lib/tables/enrichment.ts +328 -0
- package/src/lib/tables/query-builder.ts +5 -2
- package/src/lib/tables/trigger-evaluator.ts +3 -2
- package/src/lib/theme.ts +71 -0
- package/src/lib/usage/ledger.ts +2 -18
- package/src/lib/util/__tests__/similarity.test.ts +106 -0
- package/src/lib/util/similarity.ts +77 -0
- package/src/lib/utils/format-timestamp.ts +24 -0
- package/src/lib/utils/stagent-paths.ts +12 -0
- package/src/lib/validators/__tests__/blueprint.test.ts +172 -0
- package/src/lib/validators/__tests__/settings.test.ts +10 -0
- package/src/lib/validators/blueprint.ts +70 -9
- package/src/lib/validators/profile.ts +2 -2
- package/src/lib/validators/settings.ts +3 -1
- package/src/lib/workflows/__tests__/delay.test.ts +196 -0
- package/src/lib/workflows/__tests__/engine.test.ts +8 -0
- package/src/lib/workflows/__tests__/loop-executor.test.ts +54 -0
- package/src/lib/workflows/__tests__/post-action.test.ts +108 -0
- package/src/lib/workflows/blueprints/instantiator.ts +22 -1
- package/src/lib/workflows/blueprints/types.ts +10 -2
- package/src/lib/workflows/delay.ts +106 -0
- package/src/lib/workflows/engine.ts +207 -4
- package/src/lib/workflows/loop-executor.ts +349 -24
- package/src/lib/workflows/post-action.ts +91 -0
- package/src/lib/workflows/types.ts +166 -1
- package/src/app/api/license/checkout/route.ts +0 -28
- package/src/app/api/license/portal/route.ts +0 -26
- package/src/app/api/license/route.ts +0 -89
- package/src/app/api/license/usage/route.ts +0 -63
- package/src/app/api/marketplace/browse/route.ts +0 -15
- package/src/app/api/marketplace/import/route.ts +0 -28
- package/src/app/api/marketplace/publish/route.ts +0 -40
- package/src/app/api/onboarding/email/route.ts +0 -53
- package/src/app/api/settings/telemetry/route.ts +0 -14
- package/src/app/api/sync/export/route.ts +0 -54
- package/src/app/api/sync/restore/route.ts +0 -37
- package/src/app/api/sync/sessions/route.ts +0 -24
- package/src/app/auth/callback/route.ts +0 -73
- package/src/app/marketplace/page.tsx +0 -19
- package/src/components/analytics/analytics-gate-card.tsx +0 -101
- package/src/components/marketplace/blueprint-card.tsx +0 -61
- package/src/components/marketplace/marketplace-browser.tsx +0 -131
- package/src/components/onboarding/email-capture-card.tsx +0 -104
- package/src/components/settings/activation-form.tsx +0 -95
- package/src/components/settings/cloud-account-section.tsx +0 -147
- package/src/components/settings/cloud-sync-section.tsx +0 -155
- package/src/components/settings/subscription-section.tsx +0 -410
- package/src/components/settings/telemetry-section.tsx +0 -80
- package/src/components/shared/premium-gate-overlay.tsx +0 -50
- package/src/components/shared/schedule-gate-dialog.tsx +0 -64
- package/src/components/shared/upgrade-banner.tsx +0 -112
- package/src/hooks/use-supabase-auth.ts +0 -79
- package/src/lib/billing/email.ts +0 -54
- package/src/lib/billing/products.ts +0 -80
- package/src/lib/billing/stripe.ts +0 -101
- package/src/lib/cloud/supabase-browser.ts +0 -32
- package/src/lib/cloud/supabase-client.ts +0 -56
- package/src/lib/license/__tests__/features.test.ts +0 -56
- package/src/lib/license/__tests__/key-format.test.ts +0 -88
- package/src/lib/license/__tests__/manager.test.ts +0 -64
- package/src/lib/license/__tests__/tier-limits.test.ts +0 -79
- package/src/lib/license/cloud-validation.ts +0 -60
- package/src/lib/license/features.ts +0 -44
- package/src/lib/license/key-format.ts +0 -101
- package/src/lib/license/limit-check.ts +0 -111
- package/src/lib/license/limit-queries.ts +0 -51
- package/src/lib/license/manager.ts +0 -345
- package/src/lib/license/notifications.ts +0 -59
- package/src/lib/license/tier-limits.ts +0 -71
- package/src/lib/marketplace/marketplace-client.ts +0 -107
- package/src/lib/sync/cloud-sync.ts +0 -235
- package/src/lib/telemetry/conversion-events.ts +0 -71
- package/src/lib/telemetry/queue.ts +0 -122
- 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
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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
|
|
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
|
+
});
|
package/src/lib/data/clear.ts
CHANGED
|
@@ -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
|
-
//
|
|
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 —
|
|
87
|
-
|
|
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(
|
|
53
|
+
expect(migrationCount.count).toBe(12);
|
|
54
54
|
migratedDb.close();
|
|
55
55
|
});
|
|
56
56
|
});
|
package/src/lib/db/bootstrap.ts
CHANGED
|
@@ -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,
|
package/src/lib/db/index.ts
CHANGED
|
@@ -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;
|
|
@@ -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
|
}
|