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
@@ -1,410 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useState, useCallback } from "react";
4
- import { Crown, BarChart3, Brain, Calendar, Zap, ExternalLink, TrendingUp, Cloud, Store } from "lucide-react";
5
- import { toast } from "sonner";
6
- import {
7
- Card,
8
- CardContent,
9
- CardDescription,
10
- CardHeader,
11
- CardTitle,
12
- } from "@/components/ui/card";
13
- import { Button } from "@/components/ui/button";
14
- import { Badge } from "@/components/ui/badge";
15
- import { Progress } from "@/components/ui/progress";
16
- import { TIER_LABELS, TIER_PRICING, type LicenseTier } from "@/lib/license/tier-limits";
17
- import { ActivationForm } from "./activation-form";
18
-
19
- interface LicenseStatus {
20
- tier: LicenseTier;
21
- status: string;
22
- email: string | null;
23
- isPremium: boolean;
24
- activatedAt: string | null;
25
- expiresAt: string | null;
26
- limits: Record<string, number>;
27
- features: Record<string, boolean>;
28
- }
29
-
30
- interface UsageData {
31
- agentMemories: { current: number; limit: number };
32
- contextVersions: { current: number; limit: number };
33
- activeSchedules: { current: number; limit: number };
34
- parallelWorkflows: { current: number; limit: number };
35
- }
36
-
37
- const TIER_BADGE_VARIANT: Record<LicenseTier, "secondary" | "default" | "destructive" | "outline"> = {
38
- community: "secondary",
39
- solo: "default",
40
- operator: "default",
41
- scale: "outline",
42
- };
43
-
44
- const USAGE_HINTS: Record<string, { sub: string; atLimit: string }> = {
45
- agentMemories: {
46
- sub: "Cumulative per profile. New writes blocked at limit — existing memories preserved.",
47
- atLimit: "At capacity — archive old memories or upgrade to continue learning.",
48
- },
49
- contextVersions: {
50
- sub: "Cumulative per profile. New proposals blocked at limit — existing versions preserved.",
51
- atLimit: "At capacity — upgrade to unlock more self-improvement cycles.",
52
- },
53
- activeSchedules: {
54
- sub: "Concurrent active schedules. Pausing a schedule frees a slot.",
55
- atLimit: "All slots used — pause an existing schedule or upgrade for more.",
56
- },
57
- parallelWorkflows: {
58
- sub: "Concurrent running workflows. Slots free when tasks complete.",
59
- atLimit: "All slots busy — wait for a task to finish or upgrade.",
60
- },
61
- };
62
-
63
- function UsageCard({
64
- icon: Icon,
65
- label,
66
- current,
67
- limit,
68
- resourceKey,
69
- }: {
70
- icon: typeof Brain;
71
- label: string;
72
- current: number;
73
- limit: number;
74
- resourceKey: string;
75
- }) {
76
- const isUnlimited = limit === -1;
77
- const ratio = isUnlimited ? 0 : (current / limit) * 100;
78
- const isWarning = !isUnlimited && ratio >= 80;
79
- const isBlocked = !isUnlimited && ratio >= 100;
80
- const hints = USAGE_HINTS[resourceKey];
81
-
82
- return (
83
- <div className="surface-card-muted rounded-lg p-3 space-y-2">
84
- <div className="flex items-center justify-between">
85
- <div className="flex items-center gap-2">
86
- <Icon className="h-3.5 w-3.5 text-muted-foreground" />
87
- <span className="text-xs font-medium">{label}</span>
88
- </div>
89
- <span className={`text-xs ${isBlocked ? "text-destructive font-medium" : "text-muted-foreground"}`}>
90
- {current} / {isUnlimited ? "∞" : limit}
91
- </span>
92
- </div>
93
- {!isUnlimited && (
94
- <Progress
95
- value={Math.min(ratio, 100)}
96
- className={`h-1.5 ${isBlocked ? "[&>div]:bg-destructive" : isWarning ? "[&>div]:bg-amber-500" : ""}`}
97
- />
98
- )}
99
- {hints && (
100
- <p className="text-[10px] leading-relaxed text-muted-foreground">
101
- {isBlocked ? hints.atLimit : hints.sub}
102
- </p>
103
- )}
104
- </div>
105
- );
106
- }
107
-
108
- function TierBenefit({ icon: Icon, text }: { icon: typeof Brain; text: string }) {
109
- return (
110
- <li className="flex items-center gap-2">
111
- <Icon className="h-3 w-3 text-primary shrink-0" />
112
- <span className="text-[11px] text-muted-foreground">{text}</span>
113
- </li>
114
- );
115
- }
116
-
117
- export function SubscriptionSection() {
118
- const [license, setLicense] = useState<LicenseStatus | null>(null);
119
- const [usage, setUsage] = useState<UsageData | null>(null);
120
- const [loading, setLoading] = useState(true);
121
- const [checkoutLoading, setCheckoutLoading] = useState(false);
122
- const [billingPeriod, setBillingPeriod] = useState<"monthly" | "annual">("monthly");
123
-
124
- const fetchStatus = useCallback(async () => {
125
- try {
126
- const [licRes, usageRes] = await Promise.all([
127
- fetch("/api/license"),
128
- fetch("/api/license/usage"),
129
- ]);
130
- if (licRes.ok) setLicense(await licRes.json());
131
- if (usageRes.ok) setUsage(await usageRes.json());
132
- } catch {
133
- // Fail silently
134
- } finally {
135
- setLoading(false);
136
- }
137
- }, []);
138
-
139
- useEffect(() => {
140
- fetchStatus();
141
-
142
- // Check for post-purchase success
143
- const params = new URLSearchParams(window.location.search);
144
- if (params.get("success") === "true") {
145
- toast.success("Subscription activated! Premium features are now available.");
146
- // Clean up URL
147
- const url = new URL(window.location.href);
148
- url.searchParams.delete("success");
149
- window.history.replaceState({}, "", url.toString());
150
- // Refresh status after a short delay to pick up the new tier
151
- setTimeout(fetchStatus, 2000);
152
- }
153
- }, [fetchStatus]);
154
-
155
- if (loading) {
156
- return (
157
- <Card>
158
- <CardHeader>
159
- <CardTitle className="flex items-center gap-2">
160
- <Crown className="h-5 w-5" />
161
- Subscription
162
- </CardTitle>
163
- </CardHeader>
164
- <CardContent>
165
- <div className="animate-pulse space-y-3">
166
- <div className="h-4 bg-muted rounded w-1/3" />
167
- <div className="h-20 bg-muted rounded" />
168
- </div>
169
- </CardContent>
170
- </Card>
171
- );
172
- }
173
-
174
- const tier = license?.tier ?? "community";
175
- const isPremium = license?.isPremium ?? false;
176
-
177
- async function handleUpgrade(targetTier: string) {
178
- setCheckoutLoading(true);
179
- try {
180
- const res = await fetch("/api/license/checkout", {
181
- method: "POST",
182
- headers: { "Content-Type": "application/json" },
183
- body: JSON.stringify({ tier: targetTier, billingPeriod }),
184
- });
185
- const data = await res.json();
186
- if (data.url) {
187
- window.location.href = data.url;
188
- } else {
189
- toast.error(data.error ?? "Failed to start checkout");
190
- }
191
- } catch {
192
- toast.error("Failed to connect to billing service");
193
- } finally {
194
- setCheckoutLoading(false);
195
- }
196
- }
197
-
198
- return (
199
- <Card>
200
- <CardHeader>
201
- <CardTitle className="flex items-center gap-2">
202
- <Crown className="h-5 w-5" />
203
- Subscription
204
- </CardTitle>
205
- <CardDescription>
206
- Manage your plan and usage limits
207
- </CardDescription>
208
- </CardHeader>
209
- <CardContent className="space-y-6">
210
- {/* Current Plan */}
211
- <div className="flex items-center justify-between">
212
- <div className="flex items-center gap-3">
213
- <Badge variant={TIER_BADGE_VARIANT[tier]}>
214
- {TIER_LABELS[tier]}
215
- </Badge>
216
- {!isPremium && (
217
- <span className="text-sm text-muted-foreground">Free forever</span>
218
- )}
219
- {license?.expiresAt && (
220
- <span className="text-sm text-muted-foreground">
221
- Renews {new Date(license.expiresAt).toLocaleDateString()}
222
- </span>
223
- )}
224
- </div>
225
- {isPremium && license?.email && (
226
- <Button variant="outline" size="sm" asChild>
227
- <a href={`/api/license/portal?email=${encodeURIComponent(license.email)}`} target="_blank" rel="noopener">
228
- Manage Billing <ExternalLink className="ml-1 h-3 w-3" />
229
- </a>
230
- </Button>
231
- )}
232
- </div>
233
-
234
- {/* Usage Summary */}
235
- {usage && (
236
- <div className="grid grid-cols-2 gap-3">
237
- <UsageCard icon={Brain} label="Agent Memories" current={usage.agentMemories.current} limit={usage.agentMemories.limit} resourceKey="agentMemories" />
238
- <UsageCard icon={BarChart3} label="Context Versions" current={usage.contextVersions.current} limit={usage.contextVersions.limit} resourceKey="contextVersions" />
239
- <UsageCard icon={Calendar} label="Active Schedules" current={usage.activeSchedules.current} limit={usage.activeSchedules.limit} resourceKey="activeSchedules" />
240
- <UsageCard icon={Zap} label="Parallel Workflows" current={usage.parallelWorkflows.current} limit={usage.parallelWorkflows.limit} resourceKey="parallelWorkflows" />
241
- </div>
242
- )}
243
-
244
- {/* Tier Comparison */}
245
- {!isPremium && (
246
- <div className="space-y-3">
247
- <div className="flex items-center justify-between">
248
- <h4 className="text-sm font-medium">Upgrade your plan</h4>
249
- <div className="flex items-center gap-1 rounded-lg border p-0.5">
250
- <button
251
- type="button"
252
- className={`px-3 py-1 text-xs rounded-md transition-colors ${billingPeriod === "monthly" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"}`}
253
- onClick={() => setBillingPeriod("monthly")}
254
- >
255
- Monthly
256
- </button>
257
- <button
258
- type="button"
259
- className={`px-3 py-1 text-xs rounded-md transition-colors ${billingPeriod === "annual" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"}`}
260
- onClick={() => setBillingPeriod("annual")}
261
- >
262
- Annual
263
- </button>
264
- </div>
265
- </div>
266
- <div className="grid grid-cols-3 gap-3">
267
- {/* Solo */}
268
- <div className={`surface-card-muted rounded-lg p-4 flex flex-col ${tier === "solo" ? "ring-1 ring-primary" : ""}`}>
269
- <div className="space-y-3 flex-1">
270
- <div>
271
- <div className="flex items-center justify-between">
272
- <span className="text-sm font-semibold">Solo</span>
273
- {tier === "solo" && <Badge variant="outline" className="text-[10px]">Current Plan</Badge>}
274
- </div>
275
- <p className="text-lg font-bold mt-1">
276
- ${TIER_PRICING.solo[billingPeriod]}
277
- <span className="text-xs font-normal text-muted-foreground">
278
- {billingPeriod === "monthly" ? "/mo" : "/yr"}
279
- </span>
280
- </p>
281
- {billingPeriod === "annual" && (
282
- <p className="text-[10px] text-primary font-medium mt-0.5">
283
- Save ${TIER_PRICING.solo.monthly * 12 - TIER_PRICING.solo.annual}/yr
284
- </p>
285
- )}
286
- </div>
287
- <p className="text-[11px] leading-relaxed text-muted-foreground">
288
- For power users who need room to grow. 4x the memory, longer history, and marketplace access.
289
- </p>
290
- <ul className="space-y-1">
291
- <TierBenefit icon={Brain} text="200 memories per profile" />
292
- <TierBenefit icon={Calendar} text="20 active schedules" />
293
- <TierBenefit icon={Store} text="Import marketplace blueprints" />
294
- </ul>
295
- </div>
296
- <Button
297
- size="sm"
298
- variant="outline"
299
- className="w-full mt-4"
300
- disabled={checkoutLoading || tier === "solo"}
301
- onClick={() => handleUpgrade("solo")}
302
- >
303
- {tier === "solo" ? "Current Plan" : checkoutLoading ? "Loading..." : "Get Solo"}
304
- </Button>
305
- </div>
306
-
307
- {/* Operator */}
308
- <div className={`surface-card-muted rounded-lg p-4 flex flex-col ${tier === "operator" ? "ring-1 ring-primary" : "ring-1 ring-primary/30"}`}>
309
- <div className="space-y-3 flex-1">
310
- <div className="flex items-center justify-between">
311
- <span className="text-sm font-semibold">Operator</span>
312
- <div className="flex items-center gap-1.5">
313
- {tier === "operator" && <Badge variant="outline" className="text-[10px]">Current Plan</Badge>}
314
- <Badge variant="secondary" className="text-[10px]">Popular</Badge>
315
- </div>
316
- </div>
317
- <p className="text-lg font-bold">
318
- ${TIER_PRICING.operator[billingPeriod]}
319
- <span className="text-xs font-normal text-muted-foreground">
320
- {billingPeriod === "monthly" ? "/mo" : "/yr"}
321
- </span>
322
- </p>
323
- {billingPeriod === "annual" && (
324
- <p className="text-[10px] text-primary font-medium">
325
- Save ${TIER_PRICING.operator.monthly * 12 - TIER_PRICING.operator.annual}/yr
326
- </p>
327
- )}
328
- <p className="text-[11px] leading-relaxed text-muted-foreground">
329
- For professionals who run AI at scale. Full analytics, cloud sync, and marketplace publishing.
330
- </p>
331
- <ul className="space-y-1">
332
- <TierBenefit icon={TrendingUp} text="ROI analytics dashboard" />
333
- <TierBenefit icon={Cloud} text="Encrypted cloud sync" />
334
- <TierBenefit icon={Store} text="Publish to marketplace" />
335
- <TierBenefit icon={Brain} text="500 memories per profile" />
336
- </ul>
337
- </div>
338
- <Button
339
- size="sm"
340
- className="w-full mt-4"
341
- disabled={checkoutLoading || tier === "operator"}
342
- onClick={() => handleUpgrade("operator")}
343
- >
344
- {tier === "operator" ? "Current Plan" : checkoutLoading ? "Loading..." : "Get Operator"}
345
- </Button>
346
- </div>
347
-
348
- {/* Scale */}
349
- <div className={`surface-card-muted rounded-lg p-4 flex flex-col ${tier === "scale" ? "ring-1 ring-primary" : ""}`}>
350
- <div className="space-y-3 flex-1">
351
- <div>
352
- <div className="flex items-center justify-between">
353
- <span className="text-sm font-semibold">Scale</span>
354
- {tier === "scale" && <Badge variant="outline" className="text-[10px]">Current Plan</Badge>}
355
- </div>
356
- <p className="text-lg font-bold mt-1">
357
- ${TIER_PRICING.scale[billingPeriod]}
358
- <span className="text-xs font-normal text-muted-foreground">
359
- {billingPeriod === "monthly" ? "/mo" : "/yr"}
360
- </span>
361
- </p>
362
- {billingPeriod === "annual" && (
363
- <p className="text-[10px] text-primary font-medium mt-0.5">
364
- Save ${TIER_PRICING.scale.monthly * 12 - TIER_PRICING.scale.annual}/yr
365
- </p>
366
- )}
367
- </div>
368
- <p className="text-[11px] leading-relaxed text-muted-foreground">
369
- No limits, no compromises. Unlimited everything with featured marketplace placement.
370
- </p>
371
- <ul className="space-y-1">
372
- <TierBenefit icon={Zap} text="Unlimited memories & schedules" />
373
- <TierBenefit icon={Store} text="Featured marketplace listings" />
374
- <TierBenefit icon={Calendar} text="Unlimited history retention" />
375
- </ul>
376
- </div>
377
- <Button
378
- size="sm"
379
- variant="outline"
380
- className="w-full mt-4"
381
- disabled={checkoutLoading || tier === "scale"}
382
- onClick={() => handleUpgrade("scale")}
383
- >
384
- {tier === "scale" ? "Current Plan" : checkoutLoading ? "Loading..." : "Get Scale"}
385
- </Button>
386
- </div>
387
- </div>
388
- </div>
389
- )}
390
-
391
- {/* License Key Activation (fallback for manual entry) */}
392
- {!isPremium && (
393
- <ActivationForm onActivated={fetchStatus} />
394
- )}
395
-
396
- {/* License Info */}
397
- {license?.email && (
398
- <p className="text-xs text-muted-foreground">
399
- Licensed to {license.email}
400
- {license.status === "grace" && (
401
- <span className="text-amber-500 ml-2">
402
- (Offline grace period — reconnect to validate)
403
- </span>
404
- )}
405
- </p>
406
- )}
407
- </CardContent>
408
- </Card>
409
- );
410
- }
@@ -1,80 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useState } from "react";
4
- import { BarChart3, Shield } from "lucide-react";
5
- import { toast } from "sonner";
6
- import {
7
- Card,
8
- CardContent,
9
- CardDescription,
10
- CardHeader,
11
- CardTitle,
12
- } from "@/components/ui/card";
13
- import { Switch } from "@/components/ui/switch";
14
-
15
- export function TelemetrySection() {
16
- const [enabled, setEnabled] = useState(false);
17
- const [loading, setLoading] = useState(true);
18
-
19
- useEffect(() => {
20
- fetch("/api/settings/telemetry")
21
- .then((r) => (r.ok ? r.json() : { enabled: false }))
22
- .then((d) => setEnabled(d.enabled === true))
23
- .catch(() => {})
24
- .finally(() => setLoading(false));
25
- }, []);
26
-
27
- async function handleToggle(checked: boolean) {
28
- setEnabled(checked);
29
- try {
30
- const res = await fetch("/api/settings/telemetry", {
31
- method: "POST",
32
- headers: { "Content-Type": "application/json" },
33
- body: JSON.stringify({ enabled: checked }),
34
- });
35
- if (res.ok) {
36
- toast.success(checked ? "Telemetry enabled — thank you!" : "Telemetry disabled");
37
- } else {
38
- setEnabled(!checked); // Revert on failure
39
- }
40
- } catch {
41
- setEnabled(!checked);
42
- }
43
- }
44
-
45
- return (
46
- <Card>
47
- <CardHeader>
48
- <CardTitle className="flex items-center gap-2">
49
- <BarChart3 className="h-5 w-5" />
50
- Anonymous Telemetry
51
- </CardTitle>
52
- <CardDescription>
53
- Help improve Stagent by sharing anonymized usage data
54
- </CardDescription>
55
- </CardHeader>
56
- <CardContent className="space-y-4">
57
- <div className="flex items-center justify-between">
58
- <div className="space-y-1">
59
- <p className="text-sm font-medium">Share usage data</p>
60
- <p className="text-xs text-muted-foreground">
61
- Activity types, model usage, and outcome rates. No task content, project names, or personal data.
62
- </p>
63
- </div>
64
- <Switch
65
- checked={enabled}
66
- onCheckedChange={handleToggle}
67
- disabled={loading}
68
- />
69
- </div>
70
- <div className="flex items-start gap-2 text-xs text-muted-foreground">
71
- <Shield className="h-3.5 w-3.5 shrink-0 mt-0.5" />
72
- <span>
73
- Data is opt-in, anonymized, and never includes task descriptions, file contents, or email addresses.
74
- You can disable this at any time.
75
- </span>
76
- </div>
77
- </CardContent>
78
- </Card>
79
- );
80
- }
@@ -1,50 +0,0 @@
1
- "use client";
2
-
3
- import Link from "next/link";
4
- import { Lock } from "lucide-react";
5
- import { Button } from "@/components/ui/button";
6
- import { cn } from "@/lib/utils";
7
- import { TIER_LABELS, type LicenseTier } from "@/lib/license/tier-limits";
8
-
9
- interface PremiumGateOverlayProps {
10
- feature: string;
11
- requiredTier: LicenseTier;
12
- children: React.ReactNode;
13
- className?: string;
14
- }
15
-
16
- /**
17
- * Content-fade overlay for Community users viewing premium features.
18
- * Shows blurred/faded content with a lock card and upgrade CTA.
19
- */
20
- export function PremiumGateOverlay({
21
- feature,
22
- requiredTier,
23
- children,
24
- className,
25
- }: PremiumGateOverlayProps) {
26
- return (
27
- <div className={cn("relative", className)}>
28
- {/* Faded content */}
29
- <div className="opacity-30 pointer-events-none select-none blur-[2px]" aria-hidden>
30
- {children}
31
- </div>
32
-
33
- {/* Lock card */}
34
- <div className="absolute inset-0 flex items-center justify-center">
35
- <div className="surface-card-muted rounded-lg border p-6 text-center space-y-3 max-w-sm shadow-lg">
36
- <Lock className="h-8 w-8 mx-auto text-muted-foreground" />
37
- <h3 className="text-sm font-medium">{feature}</h3>
38
- <p className="text-xs text-muted-foreground">
39
- This feature requires the {TIER_LABELS[requiredTier]} tier or above.
40
- </p>
41
- <Button size="sm" asChild>
42
- <Link href={`/settings?highlight=${requiredTier}`}>
43
- Upgrade to {TIER_LABELS[requiredTier]}
44
- </Link>
45
- </Button>
46
- </div>
47
- </div>
48
- </div>
49
- );
50
- }
@@ -1,64 +0,0 @@
1
- "use client";
2
-
3
- import Link from "next/link";
4
- import { CalendarOff } from "lucide-react";
5
- import { Button } from "@/components/ui/button";
6
- import {
7
- Dialog,
8
- DialogContent,
9
- DialogDescription,
10
- DialogFooter,
11
- DialogHeader,
12
- DialogTitle,
13
- } from "@/components/ui/dialog";
14
- import { TIER_LABELS, type LicenseTier } from "@/lib/license/tier-limits";
15
-
16
- interface ScheduleGateDialogProps {
17
- open: boolean;
18
- onClose: () => void;
19
- current: number;
20
- max: number;
21
- requiredTier: string;
22
- }
23
-
24
- /**
25
- * Modal intercept when a Community user tries to create a schedule
26
- * past their limit. Shows clear messaging and an upgrade path.
27
- */
28
- export function ScheduleGateDialog({
29
- open,
30
- onClose,
31
- current,
32
- max,
33
- requiredTier,
34
- }: ScheduleGateDialogProps) {
35
- const tierLabel = TIER_LABELS[requiredTier as LicenseTier] ?? requiredTier;
36
-
37
- return (
38
- <Dialog open={open} onOpenChange={(v) => !v && onClose()}>
39
- <DialogContent className="sm:max-w-md">
40
- <DialogHeader>
41
- <div className="flex items-center gap-2">
42
- <CalendarOff className="h-5 w-5 text-muted-foreground" />
43
- <DialogTitle>Schedule limit reached</DialogTitle>
44
- </div>
45
- <DialogDescription>
46
- You have {current} of {max} active schedules on the{" "}
47
- {TIER_LABELS.community} tier. Upgrade to {tierLabel} for up to{" "}
48
- {requiredTier === "scale" ? "unlimited" : "more"} active schedules.
49
- </DialogDescription>
50
- </DialogHeader>
51
- <DialogFooter className="flex gap-2 sm:gap-0">
52
- <Button variant="outline" onClick={onClose}>
53
- Cancel
54
- </Button>
55
- <Button asChild>
56
- <Link href={`/settings?highlight=${requiredTier}`}>
57
- Upgrade to {tierLabel}
58
- </Link>
59
- </Button>
60
- </DialogFooter>
61
- </DialogContent>
62
- </Dialog>
63
- );
64
- }