stagent 0.4.0 → 0.6.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 +67 -31
- package/dist/cli.js +151 -2
- package/docs/.coverage-gaps.json +21 -0
- package/docs/.last-generated +1 -1
- package/docs/features/agent-intelligence.md +36 -14
- package/docs/features/chat.md +53 -71
- package/docs/features/cost-usage.md +14 -10
- package/docs/features/dashboard-kanban.md +30 -13
- package/docs/features/delivery-channels.md +198 -0
- package/docs/features/design-system.md +10 -10
- package/docs/features/documents.md +8 -8
- package/docs/features/home-workspace.md +20 -15
- package/docs/features/inbox-notifications.md +22 -10
- package/docs/features/keyboard-navigation.md +11 -11
- package/docs/features/monitoring.md +1 -1
- package/docs/features/playbook.md +30 -32
- package/docs/features/profiles.md +33 -11
- package/docs/features/projects.md +2 -2
- package/docs/features/provider-runtimes.md +58 -14
- package/docs/features/schedules.md +77 -41
- package/docs/features/settings.md +134 -51
- package/docs/features/shared-components.md +7 -15
- package/docs/features/tool-permissions.md +9 -9
- package/docs/features/workflows.md +32 -21
- package/docs/getting-started.md +33 -9
- package/docs/index.md +25 -16
- package/docs/journeys/developer.md +124 -207
- package/docs/journeys/personal-use.md +70 -79
- package/docs/journeys/power-user.md +107 -151
- package/docs/journeys/work-use.md +81 -113
- package/docs/manifest.json +79 -47
- package/docs/superpowers/plans/2026-03-30-finish-in-progress-features.md +547 -0
- package/docs/superpowers/specs/2026-03-27-chat-screenshot-display-design.md +303 -0
- package/docs/use-cases/agency-operator.md +84 -0
- package/docs/use-cases/solo-founder.md +75 -0
- package/docs/why-stagent.md +59 -0
- package/package.json +12 -3
- package/src/app/api/channels/[id]/route.ts +103 -0
- package/src/app/api/channels/[id]/test/route.ts +52 -0
- package/src/app/api/channels/inbound/slack/route.ts +109 -0
- package/src/app/api/channels/inbound/telegram/poll/route.ts +128 -0
- package/src/app/api/channels/inbound/telegram/route.ts +76 -0
- package/src/app/api/channels/route.ts +71 -0
- package/src/app/api/chat/conversations/[id]/messages/route.ts +3 -2
- package/src/app/api/chat/conversations/route.ts +15 -0
- package/src/app/api/chat/entities/search/route.ts +112 -0
- package/src/app/api/documents/[id]/file/route.ts +4 -1
- package/src/app/api/environment/profiles/suggest/route.ts +19 -3
- package/src/app/api/environment/scan/route.ts +8 -1
- package/src/app/api/handoffs/[id]/route.ts +76 -0
- package/src/app/api/handoffs/route.ts +89 -0
- package/src/app/api/memory/route.ts +181 -0
- package/src/app/api/profiles/[id]/route.ts +16 -1
- package/src/app/api/profiles/[id]/test/route.ts +4 -0
- package/src/app/api/profiles/[id]/test-results/route.ts +22 -0
- package/src/app/api/profiles/[id]/test-single/route.ts +64 -0
- package/src/app/api/profiles/assist/route.ts +35 -0
- package/src/app/api/profiles/import-repo/apply-updates/route.ts +123 -0
- package/src/app/api/profiles/import-repo/check-updates/route.ts +163 -0
- package/src/app/api/profiles/import-repo/confirm/route.ts +118 -0
- package/src/app/api/profiles/import-repo/preview/route.ts +107 -0
- package/src/app/api/profiles/import-repo/route.ts +29 -0
- package/src/app/api/profiles/import-repo/scan/route.ts +25 -0
- package/src/app/api/profiles/route.ts +73 -22
- package/src/app/api/projects/[id]/route.ts +119 -9
- package/src/app/api/projects/__tests__/delete-project.test.ts +170 -0
- package/src/app/api/runtimes/ollama/route.ts +86 -0
- package/src/app/api/runtimes/suggest/route.ts +29 -0
- package/src/app/api/schedules/[id]/heartbeat-history/route.ts +77 -0
- package/src/app/api/schedules/[id]/route.ts +41 -3
- package/src/app/api/schedules/parse/route.ts +66 -0
- package/src/app/api/schedules/route.ts +71 -12
- package/src/app/api/settings/author-default/route.ts +7 -0
- package/src/app/api/settings/browser-tools/route.ts +68 -0
- package/src/app/api/settings/learning/route.ts +41 -0
- package/src/app/api/settings/ollama/route.ts +34 -0
- package/src/app/api/settings/providers/route.ts +57 -0
- package/src/app/api/settings/routing/route.ts +24 -0
- package/src/app/api/settings/web-search/route.ts +28 -0
- package/src/app/api/tasks/[id]/execute/route.ts +13 -1
- package/src/app/documents/page.tsx +3 -0
- package/src/app/environment/page.tsx +8 -1
- package/src/app/settings/page.tsx +12 -4
- package/src/app/workflows/[id]/edit/page.tsx +2 -0
- package/src/app/workflows/new/page.tsx +2 -0
- package/src/components/chat/chat-command-popover.tsx +280 -0
- package/src/components/chat/chat-input.tsx +90 -10
- package/src/components/chat/chat-message.tsx +9 -3
- package/src/components/chat/chat-model-selector.tsx +42 -1
- package/src/components/chat/chat-shell.tsx +31 -5
- package/src/components/chat/screenshot-gallery.tsx +96 -0
- package/src/components/dashboard/welcome-landing.tsx +9 -9
- package/src/components/environment/artifact-card.tsx +27 -1
- package/src/components/environment/environment-dashboard.tsx +50 -2
- package/src/components/environment/environment-summary-card.tsx +5 -2
- package/src/components/environment/suggested-profiles.tsx +117 -52
- package/src/components/handoffs/handoff-approval-card.tsx +159 -0
- package/src/components/memory/memory-browser.tsx +315 -0
- package/src/components/monitoring/log-entry.tsx +61 -27
- package/src/components/profiles/learned-context-panel.tsx +4 -4
- package/src/components/profiles/profile-assist-panel.tsx +512 -0
- package/src/components/profiles/profile-browser.tsx +109 -8
- package/src/components/profiles/profile-card.tsx +29 -1
- package/src/components/profiles/profile-detail-view.tsx +200 -28
- package/src/components/profiles/profile-form-view.tsx +220 -82
- package/src/components/profiles/repo-import-wizard.tsx +648 -0
- package/src/components/profiles/smoke-test-editor.tsx +106 -0
- package/src/components/projects/project-detail.tsx +15 -2
- package/src/components/schedules/schedule-create-sheet.tsx +32 -330
- package/src/components/schedules/schedule-detail-sheet.tsx +37 -21
- package/src/components/schedules/schedule-edit-sheet.tsx +159 -0
- package/src/components/schedules/schedule-form.tsx +749 -0
- package/src/components/schedules/schedule-list.tsx +31 -2
- package/src/components/settings/auth-method-selector.tsx +7 -1
- package/src/components/settings/browser-tools-section.tsx +247 -0
- package/src/components/settings/budget-guardrails-section.tsx +111 -48
- package/src/components/settings/channels-section.tsx +526 -0
- package/src/components/settings/chat-settings-section.tsx +27 -1
- package/src/components/settings/data-management-section.tsx +8 -6
- package/src/components/settings/learning-context-section.tsx +124 -0
- package/src/components/settings/ollama-section.tsx +270 -0
- package/src/components/settings/providers-runtimes-section.tsx +499 -0
- package/src/components/settings/runtime-timeout-section.tsx +4 -4
- package/src/components/settings/web-search-section.tsx +101 -0
- package/src/components/shared/command-palette.tsx +1 -30
- package/src/components/shared/screenshot-lightbox.tsx +151 -0
- package/src/components/shared/tag-input.tsx +156 -0
- package/src/components/tasks/kanban-board.tsx +32 -0
- package/src/components/tasks/kanban-column.tsx +4 -2
- package/src/components/tasks/task-card.tsx +1 -0
- package/src/components/tasks/task-chip-bar.tsx +6 -1
- package/src/components/tasks/task-create-panel.tsx +55 -5
- package/src/components/workflows/workflow-form-view.tsx +38 -3
- package/src/hooks/use-caret-position.ts +104 -0
- package/src/hooks/use-chat-autocomplete.ts +288 -0
- package/src/hooks/use-project-skills.ts +66 -0
- package/src/hooks/use-tag-suggestions.ts +31 -0
- package/src/instrumentation.ts +4 -1
- package/src/lib/agents/__tests__/browser-mcp.test.ts +175 -0
- package/src/lib/agents/__tests__/claude-agent.test.ts +6 -0
- package/src/lib/agents/__tests__/learned-context.test.ts +10 -0
- package/src/lib/agents/agentic-loop.ts +235 -0
- package/src/lib/agents/browser-mcp.ts +174 -0
- package/src/lib/agents/claude-agent.ts +83 -198
- package/src/lib/agents/handoff/bus.ts +164 -0
- package/src/lib/agents/handoff/governance.ts +47 -0
- package/src/lib/agents/handoff/types.ts +16 -0
- package/src/lib/agents/learned-context.ts +27 -7
- package/src/lib/agents/memory/decay.ts +61 -0
- package/src/lib/agents/memory/extractor.ts +181 -0
- package/src/lib/agents/memory/retrieval.ts +96 -0
- package/src/lib/agents/memory/types.ts +6 -0
- package/src/lib/agents/profiles/__tests__/project-profiles.test.ts +119 -0
- package/src/lib/agents/profiles/__tests__/registry.test.ts +11 -3
- package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/content-creator/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/content-creator/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/customer-support-agent/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/customer-support-agent/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/financial-analyst/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/financial-analyst/profile.yaml +24 -0
- package/src/lib/agents/profiles/builtins/general/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/marketing-strategist/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/marketing-strategist/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/operations-coordinator/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/operations-coordinator/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/researcher/SKILL.md +1 -0
- package/src/lib/agents/profiles/builtins/researcher/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/sales-researcher/SKILL.md +19 -0
- package/src/lib/agents/profiles/builtins/sales-researcher/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +1 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/sweep/profile.yaml +1 -1
- package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +2 -0
- package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +2 -2
- package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +2 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +2 -2
- package/src/lib/agents/profiles/project-profiles.ts +193 -0
- package/src/lib/agents/profiles/registry.ts +130 -6
- package/src/lib/agents/profiles/types.ts +28 -0
- package/src/lib/agents/router.ts +174 -2
- package/src/lib/agents/runtime/__tests__/catalog.test.ts +15 -4
- package/src/lib/agents/runtime/anthropic-direct.ts +644 -0
- package/src/lib/agents/runtime/catalog.ts +57 -2
- package/src/lib/agents/runtime/claude.ts +205 -1
- package/src/lib/agents/runtime/index.ts +22 -0
- package/src/lib/agents/runtime/ollama-adapter.ts +409 -0
- package/src/lib/agents/runtime/openai-direct.ts +514 -0
- package/src/lib/agents/runtime/profile-assist-types.ts +30 -0
- package/src/lib/agents/runtime/types.ts +2 -0
- package/src/lib/agents/tool-permissions.ts +203 -0
- package/src/lib/channels/gateway.ts +321 -0
- package/src/lib/channels/poller.ts +268 -0
- package/src/lib/channels/registry.ts +90 -0
- package/src/lib/channels/slack-adapter.ts +188 -0
- package/src/lib/channels/telegram-adapter.ts +218 -0
- package/src/lib/channels/types.ts +43 -0
- package/src/lib/channels/webhook-adapter.ts +74 -0
- package/src/lib/chat/command-data.ts +50 -0
- package/src/lib/chat/context-builder.ts +147 -3
- package/src/lib/chat/engine.ts +182 -19
- package/src/lib/chat/ollama-engine.ts +198 -0
- package/src/lib/chat/slash-commands.ts +191 -0
- package/src/lib/chat/stagent-tools.ts +106 -20
- package/src/lib/chat/tool-catalog.ts +209 -0
- package/src/lib/chat/tool-registry.ts +90 -0
- package/src/lib/chat/tools/chat-history-tools.ts +4 -4
- package/src/lib/chat/tools/document-tools.ts +43 -6
- package/src/lib/chat/tools/handoff-tools.ts +70 -0
- package/src/lib/chat/tools/notification-tools.ts +4 -4
- package/src/lib/chat/tools/profile-tools.ts +3 -3
- package/src/lib/chat/tools/project-tools.ts +3 -3
- package/src/lib/chat/tools/schedule-tools.ts +29 -13
- package/src/lib/chat/tools/settings-tools.ts +2 -2
- package/src/lib/chat/tools/task-tools.ts +66 -11
- package/src/lib/chat/tools/usage-tools.ts +2 -2
- package/src/lib/chat/tools/workflow-tools.ts +8 -8
- package/src/lib/chat/types.ts +22 -6
- package/src/lib/constants/known-tools.ts +19 -0
- package/src/lib/constants/prose-styles.ts +1 -1
- package/src/lib/constants/settings.ts +11 -0
- package/src/lib/data/channel-bindings.ts +85 -0
- package/src/lib/data/clear.ts +38 -4
- package/src/lib/data/profile-test-results.ts +48 -0
- package/src/lib/data/seed-data/conversations.ts +196 -0
- package/src/lib/data/seed-data/learned-context.ts +99 -0
- package/src/lib/data/seed-data/notifications.ts +54 -1
- package/src/lib/data/seed-data/profile-test-results.ts +96 -0
- package/src/lib/data/seed-data/repo-imports.ts +51 -0
- package/src/lib/data/seed-data/views.ts +60 -0
- package/src/lib/data/seed.ts +51 -0
- package/src/lib/db/bootstrap.ts +167 -0
- package/src/lib/db/migrations/0012_add_screenshot_columns.sql +5 -0
- package/src/lib/db/migrations/0013_add_repo_imports.sql +15 -0
- package/src/lib/db/migrations/0014_add_linked_profile_id.sql +3 -0
- package/src/lib/db/migrations/0015_add_channel_bindings.sql +23 -0
- package/src/lib/db/schema.ts +192 -1
- package/src/lib/environment/__tests__/auto-scan.test.ts +86 -0
- package/src/lib/environment/__tests__/profile-linker.test.ts +187 -0
- package/src/lib/environment/auto-scan.ts +48 -0
- package/src/lib/environment/data.ts +25 -0
- package/src/lib/environment/profile-generator.ts +40 -10
- package/src/lib/environment/profile-linker.ts +143 -0
- package/src/lib/environment/profile-rules.ts +96 -0
- package/src/lib/import/dedup.ts +149 -0
- package/src/lib/import/format-adapter.ts +631 -0
- package/src/lib/import/github-api.ts +219 -0
- package/src/lib/import/repo-scanner.ts +251 -0
- package/src/lib/schedules/__tests__/nlp-parser.test.ts +330 -0
- package/src/lib/schedules/active-hours.ts +120 -0
- package/src/lib/schedules/heartbeat-parser.ts +224 -0
- package/src/lib/schedules/heartbeat-prompt.ts +153 -0
- package/src/lib/schedules/nlp-parser.ts +357 -0
- package/src/lib/schedules/scheduler.ts +218 -3
- package/src/lib/screenshots/__tests__/persist.test.ts +104 -0
- package/src/lib/screenshots/persist.ts +114 -0
- package/src/lib/settings/__tests__/budget-guardrails.test.ts +39 -1
- package/src/lib/settings/helpers.ts +6 -0
- package/src/lib/settings/routing.ts +24 -0
- package/src/lib/settings/runtime-setup.ts +28 -1
- package/src/lib/usage/ledger.ts +2 -1
- package/src/lib/utils/stagent-paths.ts +4 -0
- package/src/lib/validators/__tests__/settings.test.ts +9 -0
- package/src/lib/validators/profile.ts +39 -0
- package/src/lib/workflows/blueprints/builtins/business-daily-briefing.yaml +102 -0
- package/src/lib/workflows/blueprints/builtins/content-marketing-pipeline.yaml +90 -0
- package/src/lib/workflows/blueprints/builtins/customer-support-triage.yaml +107 -0
- package/src/lib/workflows/blueprints/builtins/financial-reporting.yaml +104 -0
- package/src/lib/workflows/blueprints/builtins/lead-research-pipeline.yaml +82 -0
|
@@ -3,6 +3,7 @@ import { db } from "@/lib/db";
|
|
|
3
3
|
import { schedules } from "@/lib/db/schema";
|
|
4
4
|
import { desc, eq } from "drizzle-orm";
|
|
5
5
|
import { parseInterval, computeNextFireTime } from "@/lib/schedules/interval-parser";
|
|
6
|
+
import { parseNaturalLanguage } from "@/lib/schedules/nlp-parser";
|
|
6
7
|
import { resolveAgentRuntime } from "@/lib/agents/runtime/catalog";
|
|
7
8
|
import { validateRuntimeProfileAssignment } from "@/lib/agents/profiles/assignment-validation";
|
|
8
9
|
|
|
@@ -27,6 +28,12 @@ export async function POST(req: NextRequest) {
|
|
|
27
28
|
recurs,
|
|
28
29
|
maxFirings,
|
|
29
30
|
expiresInHours,
|
|
31
|
+
type,
|
|
32
|
+
heartbeatChecklist,
|
|
33
|
+
activeHoursStart,
|
|
34
|
+
activeHoursEnd,
|
|
35
|
+
activeTimezone,
|
|
36
|
+
heartbeatBudgetPerDay,
|
|
30
37
|
} =
|
|
31
38
|
body as {
|
|
32
39
|
name?: string;
|
|
@@ -38,27 +45,64 @@ export async function POST(req: NextRequest) {
|
|
|
38
45
|
recurs?: boolean;
|
|
39
46
|
maxFirings?: number;
|
|
40
47
|
expiresInHours?: number;
|
|
48
|
+
type?: "scheduled" | "heartbeat";
|
|
49
|
+
heartbeatChecklist?: Array<{ id: string; instruction: string; priority: string }>;
|
|
50
|
+
activeHoursStart?: number;
|
|
51
|
+
activeHoursEnd?: number;
|
|
52
|
+
activeTimezone?: string;
|
|
53
|
+
heartbeatBudgetPerDay?: number;
|
|
41
54
|
};
|
|
42
55
|
|
|
56
|
+
const scheduleType = type ?? "scheduled";
|
|
57
|
+
|
|
43
58
|
if (!name?.trim()) {
|
|
44
59
|
return NextResponse.json({ error: "Name is required" }, { status: 400 });
|
|
45
60
|
}
|
|
46
|
-
if (!prompt?.trim()) {
|
|
47
|
-
return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
|
|
48
|
-
}
|
|
49
61
|
if (!interval?.trim()) {
|
|
50
62
|
return NextResponse.json({ error: "Interval is required" }, { status: 400 });
|
|
51
63
|
}
|
|
52
64
|
|
|
53
|
-
//
|
|
65
|
+
// Heartbeat-specific validation
|
|
66
|
+
if (scheduleType === "heartbeat") {
|
|
67
|
+
if (!heartbeatChecklist || heartbeatChecklist.length === 0) {
|
|
68
|
+
return NextResponse.json(
|
|
69
|
+
{ error: "Heartbeat schedules require at least one checklist item" },
|
|
70
|
+
{ status: 400 }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (activeHoursStart !== undefined && (activeHoursStart < 0 || activeHoursStart > 23)) {
|
|
74
|
+
return NextResponse.json(
|
|
75
|
+
{ error: "Active hours start must be 0-23" },
|
|
76
|
+
{ status: 400 }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (activeHoursEnd !== undefined && (activeHoursEnd < 0 || activeHoursEnd > 23)) {
|
|
80
|
+
return NextResponse.json(
|
|
81
|
+
{ error: "Active hours end must be 0-23" },
|
|
82
|
+
{ status: 400 }
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// For heartbeat type, prompt is optional (auto-generated from checklist)
|
|
88
|
+
if (scheduleType === "scheduled" && !prompt?.trim()) {
|
|
89
|
+
return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Parse interval into cron expression — try NLP first, then shorthand/cron
|
|
54
93
|
let cronExpression: string;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
94
|
+
const nlResult = parseNaturalLanguage(interval);
|
|
95
|
+
if (nlResult) {
|
|
96
|
+
cronExpression = nlResult.cronExpression;
|
|
97
|
+
} else {
|
|
98
|
+
try {
|
|
99
|
+
cronExpression = parseInterval(interval);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
return NextResponse.json(
|
|
102
|
+
{ error: (err as Error).message },
|
|
103
|
+
{ status: 400 }
|
|
104
|
+
);
|
|
105
|
+
}
|
|
62
106
|
}
|
|
63
107
|
|
|
64
108
|
if (assignedAgent !== undefined && assignedAgent !== null && assignedAgent !== "") {
|
|
@@ -90,10 +134,15 @@ export async function POST(req: NextRequest) {
|
|
|
90
134
|
? new Date(now.getTime() + expiresInHours * 60 * 60 * 1000)
|
|
91
135
|
: null;
|
|
92
136
|
|
|
137
|
+
// For heartbeat schedules, use a placeholder prompt (actual prompt is built at runtime from checklist)
|
|
138
|
+
const effectivePrompt = scheduleType === "heartbeat"
|
|
139
|
+
? (prompt?.trim() || `Heartbeat check: ${name?.trim()}`)
|
|
140
|
+
: prompt!.trim();
|
|
141
|
+
|
|
93
142
|
await db.insert(schedules).values({
|
|
94
143
|
id,
|
|
95
144
|
name: name.trim(),
|
|
96
|
-
prompt:
|
|
145
|
+
prompt: effectivePrompt,
|
|
97
146
|
cronExpression,
|
|
98
147
|
projectId: projectId || null,
|
|
99
148
|
assignedAgent: assignedAgent || null,
|
|
@@ -104,6 +153,16 @@ export async function POST(req: NextRequest) {
|
|
|
104
153
|
firingCount: 0,
|
|
105
154
|
expiresAt,
|
|
106
155
|
nextFireAt,
|
|
156
|
+
type: scheduleType,
|
|
157
|
+
heartbeatChecklist: heartbeatChecklist ? JSON.stringify(heartbeatChecklist) : null,
|
|
158
|
+
activeHoursStart: activeHoursStart ?? null,
|
|
159
|
+
activeHoursEnd: activeHoursEnd ?? null,
|
|
160
|
+
activeTimezone: activeTimezone ?? "UTC",
|
|
161
|
+
suppressionCount: 0,
|
|
162
|
+
lastActionAt: null,
|
|
163
|
+
heartbeatBudgetPerDay: heartbeatBudgetPerDay ?? null,
|
|
164
|
+
heartbeatSpentToday: 0,
|
|
165
|
+
heartbeatBudgetResetAt: null,
|
|
107
166
|
createdAt: now,
|
|
108
167
|
updatedAt: now,
|
|
109
168
|
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getSetting, setSetting } from "@/lib/settings/helpers";
|
|
3
|
+
import { SETTINGS_KEYS } from "@/lib/constants/settings";
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
const [chromeEnabled, playwrightEnabled, chromeConfig, playwrightConfig] =
|
|
7
|
+
await Promise.all([
|
|
8
|
+
getSetting(SETTINGS_KEYS.BROWSER_MCP_CHROME_DEVTOOLS_ENABLED),
|
|
9
|
+
getSetting(SETTINGS_KEYS.BROWSER_MCP_PLAYWRIGHT_ENABLED),
|
|
10
|
+
getSetting(SETTINGS_KEYS.BROWSER_MCP_CHROME_DEVTOOLS_CONFIG),
|
|
11
|
+
getSetting(SETTINGS_KEYS.BROWSER_MCP_PLAYWRIGHT_CONFIG),
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
return NextResponse.json({
|
|
15
|
+
chromeDevtoolsEnabled: chromeEnabled === "true",
|
|
16
|
+
playwrightEnabled: playwrightEnabled === "true",
|
|
17
|
+
chromeDevtoolsConfig: chromeConfig ?? "",
|
|
18
|
+
playwrightConfig: playwrightConfig ?? "",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function POST(req: NextRequest) {
|
|
23
|
+
const body = await req.json();
|
|
24
|
+
|
|
25
|
+
if (body.chromeDevtoolsEnabled !== undefined) {
|
|
26
|
+
await setSetting(
|
|
27
|
+
SETTINGS_KEYS.BROWSER_MCP_CHROME_DEVTOOLS_ENABLED,
|
|
28
|
+
body.chromeDevtoolsEnabled ? "true" : "false"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (body.playwrightEnabled !== undefined) {
|
|
33
|
+
await setSetting(
|
|
34
|
+
SETTINGS_KEYS.BROWSER_MCP_PLAYWRIGHT_ENABLED,
|
|
35
|
+
body.playwrightEnabled ? "true" : "false"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (body.chromeDevtoolsConfig !== undefined) {
|
|
40
|
+
await setSetting(
|
|
41
|
+
SETTINGS_KEYS.BROWSER_MCP_CHROME_DEVTOOLS_CONFIG,
|
|
42
|
+
body.chromeDevtoolsConfig
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (body.playwrightConfig !== undefined) {
|
|
47
|
+
await setSetting(
|
|
48
|
+
SETTINGS_KEYS.BROWSER_MCP_PLAYWRIGHT_CONFIG,
|
|
49
|
+
body.playwrightConfig
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Return updated state
|
|
54
|
+
const [chromeEnabled, playwrightEnabled, chromeConfig, playwrightConfig] =
|
|
55
|
+
await Promise.all([
|
|
56
|
+
getSetting(SETTINGS_KEYS.BROWSER_MCP_CHROME_DEVTOOLS_ENABLED),
|
|
57
|
+
getSetting(SETTINGS_KEYS.BROWSER_MCP_PLAYWRIGHT_ENABLED),
|
|
58
|
+
getSetting(SETTINGS_KEYS.BROWSER_MCP_CHROME_DEVTOOLS_CONFIG),
|
|
59
|
+
getSetting(SETTINGS_KEYS.BROWSER_MCP_PLAYWRIGHT_CONFIG),
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
return NextResponse.json({
|
|
63
|
+
chromeDevtoolsEnabled: chromeEnabled === "true",
|
|
64
|
+
playwrightEnabled: playwrightEnabled === "true",
|
|
65
|
+
chromeDevtoolsConfig: chromeConfig ?? "",
|
|
66
|
+
playwrightConfig: playwrightConfig ?? "",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getSetting, setSetting } from "@/lib/settings/helpers";
|
|
3
|
+
import { SETTINGS_KEYS } from "@/lib/constants/settings";
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
const contextCharLimit = await getSetting(
|
|
7
|
+
SETTINGS_KEYS.LEARNING_CONTEXT_CHAR_LIMIT
|
|
8
|
+
);
|
|
9
|
+
return NextResponse.json({
|
|
10
|
+
contextCharLimit: contextCharLimit ?? "8000",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function POST(req: NextRequest) {
|
|
15
|
+
const body = await req.json();
|
|
16
|
+
|
|
17
|
+
if (body.contextCharLimit !== undefined) {
|
|
18
|
+
const limit = parseInt(body.contextCharLimit, 10);
|
|
19
|
+
if (isNaN(limit) || limit < 2000 || limit > 32000 || limit % 1000 !== 0) {
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{
|
|
22
|
+
error:
|
|
23
|
+
"contextCharLimit must be between 2,000 and 32,000 (step 1,000)",
|
|
24
|
+
},
|
|
25
|
+
{ status: 400 }
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
await setSetting(
|
|
29
|
+
SETTINGS_KEYS.LEARNING_CONTEXT_CHAR_LIMIT,
|
|
30
|
+
String(limit)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const contextCharLimit = await getSetting(
|
|
35
|
+
SETTINGS_KEYS.LEARNING_CONTEXT_CHAR_LIMIT
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return NextResponse.json({
|
|
39
|
+
contextCharLimit: contextCharLimit ?? "8000",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getSetting, setSetting } from "@/lib/settings/helpers";
|
|
3
|
+
import { SETTINGS_KEYS } from "@/lib/constants/settings";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* GET /api/settings/ollama — Read Ollama settings.
|
|
7
|
+
*/
|
|
8
|
+
export async function GET() {
|
|
9
|
+
const baseUrl = await getSetting(SETTINGS_KEYS.OLLAMA_BASE_URL);
|
|
10
|
+
const defaultModel = await getSetting(SETTINGS_KEYS.OLLAMA_DEFAULT_MODEL);
|
|
11
|
+
|
|
12
|
+
return NextResponse.json({
|
|
13
|
+
baseUrl: baseUrl || "http://localhost:11434",
|
|
14
|
+
defaultModel: defaultModel || "",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* POST /api/settings/ollama — Update Ollama settings.
|
|
20
|
+
* Body: { baseUrl?: string, defaultModel?: string }
|
|
21
|
+
*/
|
|
22
|
+
export async function POST(req: NextRequest) {
|
|
23
|
+
const body = await req.json();
|
|
24
|
+
|
|
25
|
+
if (body.baseUrl !== undefined) {
|
|
26
|
+
await setSetting(SETTINGS_KEYS.OLLAMA_BASE_URL, body.baseUrl);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (body.defaultModel !== undefined) {
|
|
30
|
+
await setSetting(SETTINGS_KEYS.OLLAMA_DEFAULT_MODEL, body.defaultModel);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return NextResponse.json({ ok: true });
|
|
34
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { getRuntimeSetupStates } from "@/lib/settings/runtime-setup";
|
|
3
|
+
import { getRoutingPreference } from "@/lib/settings/routing";
|
|
4
|
+
import { getAuthSettings } from "@/lib/settings/auth";
|
|
5
|
+
import { getOpenAIAuthSettings } from "@/lib/settings/openai-auth";
|
|
6
|
+
|
|
7
|
+
export async function GET() {
|
|
8
|
+
const [runtimeStates, routingPreference, anthropicAuth, openaiAuth] =
|
|
9
|
+
await Promise.all([
|
|
10
|
+
getRuntimeSetupStates(),
|
|
11
|
+
getRoutingPreference(),
|
|
12
|
+
getAuthSettings(),
|
|
13
|
+
getOpenAIAuthSettings(),
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const anthropicConfigured =
|
|
17
|
+
runtimeStates["claude-code"].configured ||
|
|
18
|
+
runtimeStates["anthropic-direct"].configured;
|
|
19
|
+
const openaiConfigured =
|
|
20
|
+
runtimeStates["openai-codex-app-server"].configured ||
|
|
21
|
+
runtimeStates["openai-direct"].configured;
|
|
22
|
+
|
|
23
|
+
// Detect dual-billing: user has OAuth (subscription) for Claude Code
|
|
24
|
+
// AND an API key (pay-as-you-go) for Anthropic Direct
|
|
25
|
+
const anthropicHasOAuth =
|
|
26
|
+
anthropicAuth.method === "oauth" || anthropicAuth.apiKeySource === "oauth";
|
|
27
|
+
const anthropicHasApiKey = anthropicAuth.hasKey;
|
|
28
|
+
const anthropicDualBilling = anthropicHasOAuth && anthropicHasApiKey;
|
|
29
|
+
|
|
30
|
+
return NextResponse.json({
|
|
31
|
+
providers: {
|
|
32
|
+
anthropic: {
|
|
33
|
+
configured: anthropicConfigured,
|
|
34
|
+
authMethod: anthropicAuth.method,
|
|
35
|
+
hasKey: anthropicAuth.hasKey,
|
|
36
|
+
apiKeySource: anthropicAuth.apiKeySource,
|
|
37
|
+
dualBilling: anthropicDualBilling,
|
|
38
|
+
runtimes: [
|
|
39
|
+
runtimeStates["claude-code"],
|
|
40
|
+
runtimeStates["anthropic-direct"],
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
openai: {
|
|
44
|
+
configured: openaiConfigured,
|
|
45
|
+
hasKey: openaiAuth.hasKey,
|
|
46
|
+
apiKeySource: openaiAuth.apiKeySource,
|
|
47
|
+
dualBilling: false,
|
|
48
|
+
runtimes: [
|
|
49
|
+
runtimeStates["openai-codex-app-server"],
|
|
50
|
+
runtimeStates["openai-direct"],
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
routingPreference,
|
|
55
|
+
configuredProviderCount: Number(anthropicConfigured) + Number(openaiConfigured),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getRoutingPreference, setRoutingPreference } from "@/lib/settings/routing";
|
|
3
|
+
import type { RoutingPreference } from "@/lib/constants/settings";
|
|
4
|
+
|
|
5
|
+
const VALID_VALUES: RoutingPreference[] = ["cost", "latency", "quality", "manual"];
|
|
6
|
+
|
|
7
|
+
export async function GET() {
|
|
8
|
+
const preference = await getRoutingPreference();
|
|
9
|
+
return NextResponse.json({ preference });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function POST(req: NextRequest) {
|
|
13
|
+
const body = await req.json();
|
|
14
|
+
|
|
15
|
+
if (!body.preference || !VALID_VALUES.includes(body.preference)) {
|
|
16
|
+
return NextResponse.json(
|
|
17
|
+
{ error: `preference must be one of: ${VALID_VALUES.join(", ")}` },
|
|
18
|
+
{ status: 400 },
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await setRoutingPreference(body.preference);
|
|
23
|
+
return NextResponse.json({ preference: body.preference });
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getSetting, setSetting } from "@/lib/settings/helpers";
|
|
3
|
+
import { SETTINGS_KEYS } from "@/lib/constants/settings";
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
const exaEnabled = await getSetting(SETTINGS_KEYS.EXA_SEARCH_MCP_ENABLED);
|
|
7
|
+
|
|
8
|
+
return NextResponse.json({
|
|
9
|
+
exaSearchEnabled: exaEnabled === "true",
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function POST(req: NextRequest) {
|
|
14
|
+
const body = await req.json();
|
|
15
|
+
|
|
16
|
+
if (body.exaSearchEnabled !== undefined) {
|
|
17
|
+
await setSetting(
|
|
18
|
+
SETTINGS_KEYS.EXA_SEARCH_MCP_ENABLED,
|
|
19
|
+
body.exaSearchEnabled ? "true" : "false"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const exaEnabled = await getSetting(SETTINGS_KEYS.EXA_SEARCH_MCP_ENABLED);
|
|
24
|
+
|
|
25
|
+
return NextResponse.json({
|
|
26
|
+
exaSearchEnabled: exaEnabled === "true",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
2
|
import { db } from "@/lib/db";
|
|
3
|
-
import { tasks } from "@/lib/db/schema";
|
|
3
|
+
import { tasks, projects } from "@/lib/db/schema";
|
|
4
4
|
import { eq, and } from "drizzle-orm";
|
|
5
5
|
import { executeTaskWithAgent, classifyTaskProfile } from "@/lib/agents/router";
|
|
6
6
|
import { DEFAULT_AGENT_RUNTIME } from "@/lib/agents/runtime/catalog";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
BudgetLimitExceededError,
|
|
10
10
|
enforceTaskBudgetGuardrails,
|
|
11
11
|
} from "@/lib/settings/budget-guardrails";
|
|
12
|
+
import { ensureFreshScan } from "@/lib/environment/auto-scan";
|
|
12
13
|
|
|
13
14
|
export async function POST(
|
|
14
15
|
_req: NextRequest,
|
|
@@ -47,6 +48,17 @@ export async function POST(
|
|
|
47
48
|
|
|
48
49
|
const task = claimed[0];
|
|
49
50
|
|
|
51
|
+
// Auto-scan environment if the task's project has a workingDirectory
|
|
52
|
+
if (task.projectId) {
|
|
53
|
+
const [project] = await db
|
|
54
|
+
.select()
|
|
55
|
+
.from(projects)
|
|
56
|
+
.where(eq(projects.id, task.projectId));
|
|
57
|
+
if (project?.workingDirectory) {
|
|
58
|
+
ensureFreshScan(project.workingDirectory, task.projectId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
50
62
|
// Auto-classify profile if none was set
|
|
51
63
|
if (!task.agentProfile) {
|
|
52
64
|
const autoProfile = classifyTaskProfile(
|
|
@@ -24,6 +24,9 @@ export default async function DocumentsPage() {
|
|
|
24
24
|
extractedText: documents.extractedText,
|
|
25
25
|
processedPath: documents.processedPath,
|
|
26
26
|
processingError: documents.processingError,
|
|
27
|
+
source: documents.source,
|
|
28
|
+
conversationId: documents.conversationId,
|
|
29
|
+
messageId: documents.messageId,
|
|
27
30
|
createdAt: documents.createdAt,
|
|
28
31
|
updatedAt: documents.updatedAt,
|
|
29
32
|
taskTitle: tasks.title,
|
|
@@ -3,11 +3,16 @@ import { listTemplates } from "@/lib/environment/templates";
|
|
|
3
3
|
import { calculateHealthScore } from "@/lib/environment/health-scoring";
|
|
4
4
|
import { EnvironmentDashboard } from "@/components/environment/environment-dashboard";
|
|
5
5
|
import { PageShell } from "@/components/shared/page-shell";
|
|
6
|
-
import { getWorkspaceContext } from "@/lib/environment/workspace-context";
|
|
6
|
+
import { getWorkspaceContext, getLaunchCwd } from "@/lib/environment/workspace-context";
|
|
7
|
+
import { ensureFreshScan } from "@/lib/environment/auto-scan";
|
|
7
8
|
|
|
8
9
|
export const dynamic = "force-dynamic";
|
|
9
10
|
|
|
10
11
|
export default async function EnvironmentPage() {
|
|
12
|
+
// Auto-scan the workspace directory if stale or missing
|
|
13
|
+
const cwd = getLaunchCwd();
|
|
14
|
+
ensureFreshScan(cwd);
|
|
15
|
+
|
|
11
16
|
const scan = getLatestScan();
|
|
12
17
|
|
|
13
18
|
if (!scan) {
|
|
@@ -18,6 +23,7 @@ export default async function EnvironmentPage() {
|
|
|
18
23
|
artifacts={[]}
|
|
19
24
|
categoryCounts={[]}
|
|
20
25
|
toolCounts={[]}
|
|
26
|
+
scanPath={cwd}
|
|
21
27
|
/>
|
|
22
28
|
</PageShell>
|
|
23
29
|
);
|
|
@@ -48,6 +54,7 @@ export default async function EnvironmentPage() {
|
|
|
48
54
|
checkpoints={checkpoints}
|
|
49
55
|
templates={templates}
|
|
50
56
|
healthScore={healthScore}
|
|
57
|
+
scanPath={cwd}
|
|
51
58
|
/>
|
|
52
59
|
</PageShell>
|
|
53
60
|
);
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { OpenAIRuntimeSection } from "@/components/settings/openai-runtime-section";
|
|
1
|
+
import { ProvidersAndRuntimesSection } from "@/components/settings/providers-runtimes-section";
|
|
3
2
|
import { PermissionsSections } from "@/components/settings/permissions-sections";
|
|
4
3
|
import { DataManagementSection } from "@/components/settings/data-management-section";
|
|
5
4
|
import { BudgetGuardrailsSection } from "@/components/settings/budget-guardrails-section";
|
|
6
5
|
import { ChatSettingsSection } from "@/components/settings/chat-settings-section";
|
|
7
6
|
import { RuntimeTimeoutSection } from "@/components/settings/runtime-timeout-section";
|
|
7
|
+
import { BrowserToolsSection } from "@/components/settings/browser-tools-section";
|
|
8
|
+
import { WebSearchSection } from "@/components/settings/web-search-section";
|
|
9
|
+
import { LearningContextSection } from "@/components/settings/learning-context-section";
|
|
10
|
+
import { OllamaSection } from "@/components/settings/ollama-section";
|
|
11
|
+
import { ChannelsSection } from "@/components/settings/channels-section";
|
|
8
12
|
import { PageShell } from "@/components/shared/page-shell";
|
|
9
13
|
|
|
10
14
|
export const dynamic = "force-dynamic";
|
|
@@ -13,10 +17,14 @@ export default function SettingsPage() {
|
|
|
13
17
|
return (
|
|
14
18
|
<PageShell title="Settings" description="Manage your Stagent configuration">
|
|
15
19
|
<div className="space-y-6">
|
|
16
|
-
<
|
|
17
|
-
<
|
|
20
|
+
<ProvidersAndRuntimesSection />
|
|
21
|
+
<OllamaSection />
|
|
18
22
|
<ChatSettingsSection />
|
|
19
23
|
<RuntimeTimeoutSection />
|
|
24
|
+
<LearningContextSection />
|
|
25
|
+
<WebSearchSection />
|
|
26
|
+
<BrowserToolsSection />
|
|
27
|
+
<ChannelsSection />
|
|
20
28
|
<BudgetGuardrailsSection />
|
|
21
29
|
<PermissionsSections />
|
|
22
30
|
<DataManagementSection />
|