create-claude-code-visualizer 0.1.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/index.js +393 -0
- package/package.json +31 -0
- package/templates/CLAUDE.md +108 -0
- package/templates/app/.env.local.example +14 -0
- package/templates/app/ecosystem.config.js +29 -0
- package/templates/app/next-env.d.ts +6 -0
- package/templates/app/next.config.ts +16 -0
- package/templates/app/package-lock.json +4581 -0
- package/templates/app/package.json +38 -0
- package/templates/app/postcss.config.js +5 -0
- package/templates/app/src/app/agents/[slug]/chat/loading.tsx +26 -0
- package/templates/app/src/app/agents/[slug]/chat/page.tsx +579 -0
- package/templates/app/src/app/agents/[slug]/loading.tsx +19 -0
- package/templates/app/src/app/agents/page.tsx +8 -0
- package/templates/app/src/app/api/agents/[slug]/capabilities/route.ts +11 -0
- package/templates/app/src/app/api/agents/[slug]/route.ts +57 -0
- package/templates/app/src/app/api/agents/route.ts +28 -0
- package/templates/app/src/app/api/ai/generate-agent/route.ts +87 -0
- package/templates/app/src/app/api/ai/improve-claude-md/route.ts +78 -0
- package/templates/app/src/app/api/ai/suggestions/route.ts +64 -0
- package/templates/app/src/app/api/ai/title/route.ts +88 -0
- package/templates/app/src/app/api/auth/role/route.ts +17 -0
- package/templates/app/src/app/api/commands/[slug]/route.ts +61 -0
- package/templates/app/src/app/api/commands/route.ts +6 -0
- package/templates/app/src/app/api/governance/costs/route.ts +117 -0
- package/templates/app/src/app/api/governance/sessions/route.ts +335 -0
- package/templates/app/src/app/api/notifications/route.ts +62 -0
- package/templates/app/src/app/api/preferences/route.ts +44 -0
- package/templates/app/src/app/api/runs/[id]/approve/route.ts +38 -0
- package/templates/app/src/app/api/runs/[id]/events/route.ts +28 -0
- package/templates/app/src/app/api/runs/[id]/metadata/route.ts +30 -0
- package/templates/app/src/app/api/runs/[id]/route.ts +21 -0
- package/templates/app/src/app/api/runs/[id]/start/route.ts +61 -0
- package/templates/app/src/app/api/runs/[id]/stop/route.ts +16 -0
- package/templates/app/src/app/api/runs/[id]/stream/route.ts +201 -0
- package/templates/app/src/app/api/runs/route.ts +95 -0
- package/templates/app/src/app/api/schedules/[id]/route.ts +81 -0
- package/templates/app/src/app/api/schedules/route.ts +75 -0
- package/templates/app/src/app/api/settings/access-logs/route.ts +33 -0
- package/templates/app/src/app/api/settings/claude-md/route.ts +44 -0
- package/templates/app/src/app/api/settings/env-keys/route.ts +271 -0
- package/templates/app/src/app/api/settings/users/route.ts +108 -0
- package/templates/app/src/app/api/skills/[slug]/route.ts +43 -0
- package/templates/app/src/app/api/skills/route.ts +6 -0
- package/templates/app/src/app/api/tools/route.ts +65 -0
- package/templates/app/src/app/api/uploads/cleanup/route.ts +29 -0
- package/templates/app/src/app/api/uploads/route.ts +77 -0
- package/templates/app/src/app/auth/callback/route.ts +19 -0
- package/templates/app/src/app/globals.css +115 -0
- package/templates/app/src/app/layout.tsx +24 -0
- package/templates/app/src/app/loading.tsx +16 -0
- package/templates/app/src/app/login/page.tsx +64 -0
- package/templates/app/src/app/not-authorized/page.tsx +33 -0
- package/templates/app/src/app/runs/page.tsx +55 -0
- package/templates/app/src/app/schedules/page.tsx +110 -0
- package/templates/app/src/app/settings/page.tsx +1294 -0
- package/templates/app/src/app/skills/page.tsx +7 -0
- package/templates/app/src/components/agent-card.tsx +58 -0
- package/templates/app/src/components/agent-grid.tsx +90 -0
- package/templates/app/src/components/auth/auth-context.tsx +79 -0
- package/templates/app/src/components/chat-thread.tsx +50 -0
- package/templates/app/src/components/chat-view.tsx +670 -0
- package/templates/app/src/components/commands-browser.tsx +349 -0
- package/templates/app/src/components/create-agent-modal.tsx +388 -0
- package/templates/app/src/components/governance-dashboard.tsx +397 -0
- package/templates/app/src/components/icons.tsx +401 -0
- package/templates/app/src/components/layout/agent-sidebar.tsx +504 -0
- package/templates/app/src/components/layout/app-shell.tsx +29 -0
- package/templates/app/src/components/layout/nav.tsx +87 -0
- package/templates/app/src/components/layout/overview-inner.tsx +14 -0
- package/templates/app/src/components/layout/profile-menu.tsx +95 -0
- package/templates/app/src/components/layout/sidebar.tsx +30 -0
- package/templates/app/src/components/markdown.tsx +57 -0
- package/templates/app/src/components/message-bar.tsx +161 -0
- package/templates/app/src/components/notifications/notification-bell.tsx +104 -0
- package/templates/app/src/components/notifications/notification-panel.tsx +116 -0
- package/templates/app/src/components/overview/overview-content.tsx +287 -0
- package/templates/app/src/components/overview/overview-context.tsx +88 -0
- package/templates/app/src/components/preferences-modal.tsx +112 -0
- package/templates/app/src/components/run-form.tsx +73 -0
- package/templates/app/src/components/run-history-table.tsx +226 -0
- package/templates/app/src/components/run-output.tsx +187 -0
- package/templates/app/src/components/schedule-form.tsx +148 -0
- package/templates/app/src/components/skills-browser.tsx +338 -0
- package/templates/app/src/components/tool-tooltip.tsx +82 -0
- package/templates/app/src/hooks/use-sse.ts +115 -0
- package/templates/app/src/instrumentation.ts +9 -0
- package/templates/app/src/lib/agent-cache.ts +19 -0
- package/templates/app/src/lib/agent-runner.ts +411 -0
- package/templates/app/src/lib/agents.ts +168 -0
- package/templates/app/src/lib/ai.ts +40 -0
- package/templates/app/src/lib/approval-store.ts +70 -0
- package/templates/app/src/lib/auth-guard.ts +116 -0
- package/templates/app/src/lib/capabilities.ts +191 -0
- package/templates/app/src/lib/line-diff.ts +96 -0
- package/templates/app/src/lib/queue.ts +22 -0
- package/templates/app/src/lib/redis.ts +12 -0
- package/templates/app/src/lib/role-permissions.ts +166 -0
- package/templates/app/src/lib/run-agent.ts +442 -0
- package/templates/app/src/lib/supabase-browser.ts +8 -0
- package/templates/app/src/lib/supabase-middleware.ts +63 -0
- package/templates/app/src/lib/supabase-server.ts +28 -0
- package/templates/app/src/lib/supabase.ts +6 -0
- package/templates/app/src/lib/tool-descriptions.ts +29 -0
- package/templates/app/src/lib/types.ts +73 -0
- package/templates/app/src/lib/typewriter-animation.ts +159 -0
- package/templates/app/src/middleware.ts +13 -0
- package/templates/app/tsconfig.json +21 -0
- package/templates/app/uploads/.gitkeep +0 -0
- package/templates/app/worker/index.ts +342 -0
- package/templates/claude/agents/ai-trends-scout.md +66 -0
- package/templates/claude/commands/add-to-todos.md +56 -0
- package/templates/claude/commands/check-todos.md +56 -0
- package/templates/claude/hooks/auto-approve-safe.sh +34 -0
- package/templates/claude/hooks/auto-format.sh +25 -0
- package/templates/claude/hooks/block-destructive.sh +32 -0
- package/templates/claude/hooks/compaction-preserver.sh +16 -0
- package/templates/claude/hooks/notify.sh +26 -0
- package/templates/claude/settings.local.json +66 -0
- package/templates/claude/skills/frontend-design/SKILL.md +127 -0
- package/templates/claude/skills/frontend-design/reference/color-and-contrast.md +132 -0
- package/templates/claude/skills/frontend-design/reference/interaction-design.md +123 -0
- package/templates/claude/skills/frontend-design/reference/motion-design.md +99 -0
- package/templates/claude/skills/frontend-design/reference/responsive-design.md +114 -0
- package/templates/claude/skills/frontend-design/reference/spatial-design.md +100 -0
- package/templates/claude/skills/frontend-design/reference/typography.md +131 -0
- package/templates/claude/skills/frontend-design/reference/ux-writing.md +107 -0
- package/templates/claude/skills/gws-admin-reports/SKILL.md +57 -0
- package/templates/claude/skills/gws-calendar/SKILL.md +108 -0
- package/templates/claude/skills/gws-calendar-agenda/SKILL.md +52 -0
- package/templates/claude/skills/gws-calendar-insert/SKILL.md +55 -0
- package/templates/claude/skills/gws-chat/SKILL.md +73 -0
- package/templates/claude/skills/gws-chat-send/SKILL.md +49 -0
- package/templates/claude/skills/gws-classroom/SKILL.md +75 -0
- package/templates/claude/skills/gws-docs/SKILL.md +48 -0
- package/templates/claude/skills/gws-docs-write/SKILL.md +49 -0
- package/templates/claude/skills/gws-drive/SKILL.md +137 -0
- package/templates/claude/skills/gws-drive-upload/SKILL.md +52 -0
- package/templates/claude/skills/gws-events/SKILL.md +67 -0
- package/templates/claude/skills/gws-events-renew/SKILL.md +48 -0
- package/templates/claude/skills/gws-events-subscribe/SKILL.md +59 -0
- package/templates/claude/skills/gws-forms/SKILL.md +45 -0
- package/templates/claude/skills/gws-gmail/SKILL.md +59 -0
- package/templates/claude/skills/gws-gmail-forward/SKILL.md +53 -0
- package/templates/claude/skills/gws-gmail-reply/SKILL.md +56 -0
- package/templates/claude/skills/gws-gmail-reply-all/SKILL.md +60 -0
- package/templates/claude/skills/gws-gmail-send/SKILL.md +55 -0
- package/templates/claude/skills/gws-gmail-triage/SKILL.md +50 -0
- package/templates/claude/skills/gws-gmail-watch/SKILL.md +58 -0
- package/templates/claude/skills/gws-keep/SKILL.md +48 -0
- package/templates/claude/skills/gws-meet/SKILL.md +51 -0
- package/templates/claude/skills/gws-modelarmor/SKILL.md +42 -0
- package/templates/claude/skills/gws-modelarmor-create-template/SKILL.md +53 -0
- package/templates/claude/skills/gws-modelarmor-sanitize-prompt/SKILL.md +48 -0
- package/templates/claude/skills/gws-modelarmor-sanitize-response/SKILL.md +48 -0
- package/templates/claude/skills/gws-people/SKILL.md +67 -0
- package/templates/claude/skills/gws-shared/SKILL.md +66 -0
- package/templates/claude/skills/gws-sheets/SKILL.md +53 -0
- package/templates/claude/skills/gws-sheets-append/SKILL.md +51 -0
- package/templates/claude/skills/gws-sheets-read/SKILL.md +47 -0
- package/templates/claude/skills/gws-slides/SKILL.md +43 -0
- package/templates/claude/skills/gws-tasks/SKILL.md +56 -0
- package/templates/claude/skills/gws-workflow/SKILL.md +44 -0
- package/templates/claude/skills/gws-workflow-email-to-task/SKILL.md +47 -0
- package/templates/claude/skills/gws-workflow-file-announce/SKILL.md +50 -0
- package/templates/claude/skills/gws-workflow-meeting-prep/SKILL.md +47 -0
- package/templates/claude/skills/gws-workflow-standup-report/SKILL.md +46 -0
- package/templates/claude/skills/gws-workflow-weekly-digest/SKILL.md +46 -0
- package/templates/claude/skills/persona-content-creator/SKILL.md +33 -0
- package/templates/claude/skills/persona-customer-support/SKILL.md +34 -0
- package/templates/claude/skills/persona-event-coordinator/SKILL.md +35 -0
- package/templates/claude/skills/persona-exec-assistant/SKILL.md +35 -0
- package/templates/claude/skills/persona-hr-coordinator/SKILL.md +33 -0
- package/templates/claude/skills/persona-it-admin/SKILL.md +30 -0
- package/templates/claude/skills/persona-project-manager/SKILL.md +35 -0
- package/templates/claude/skills/persona-researcher/SKILL.md +33 -0
- package/templates/claude/skills/persona-sales-ops/SKILL.md +35 -0
- package/templates/claude/skills/persona-team-lead/SKILL.md +36 -0
- package/templates/claude/skills/recipe-backup-sheet-as-csv/SKILL.md +25 -0
- package/templates/claude/skills/recipe-batch-invite-to-event/SKILL.md +25 -0
- package/templates/claude/skills/recipe-block-focus-time/SKILL.md +24 -0
- package/templates/claude/skills/recipe-bulk-download-folder/SKILL.md +25 -0
- package/templates/claude/skills/recipe-collect-form-responses/SKILL.md +25 -0
- package/templates/claude/skills/recipe-compare-sheet-tabs/SKILL.md +25 -0
- package/templates/claude/skills/recipe-copy-sheet-for-new-month/SKILL.md +25 -0
- package/templates/claude/skills/recipe-create-classroom-course/SKILL.md +25 -0
- package/templates/claude/skills/recipe-create-doc-from-template/SKILL.md +29 -0
- package/templates/claude/skills/recipe-create-events-from-sheet/SKILL.md +24 -0
- package/templates/claude/skills/recipe-create-expense-tracker/SKILL.md +26 -0
- package/templates/claude/skills/recipe-create-feedback-form/SKILL.md +25 -0
- package/templates/claude/skills/recipe-create-gmail-filter/SKILL.md +26 -0
- package/templates/claude/skills/recipe-create-meet-space/SKILL.md +25 -0
- package/templates/claude/skills/recipe-create-presentation/SKILL.md +25 -0
- package/templates/claude/skills/recipe-create-shared-drive/SKILL.md +25 -0
- package/templates/claude/skills/recipe-create-task-list/SKILL.md +26 -0
- package/templates/claude/skills/recipe-create-vacation-responder/SKILL.md +25 -0
- package/templates/claude/skills/recipe-draft-email-from-doc/SKILL.md +25 -0
- package/templates/claude/skills/recipe-email-drive-link/SKILL.md +25 -0
- package/templates/claude/skills/recipe-find-free-time/SKILL.md +25 -0
- package/templates/claude/skills/recipe-find-large-files/SKILL.md +24 -0
- package/templates/claude/skills/recipe-forward-labeled-emails/SKILL.md +27 -0
- package/templates/claude/skills/recipe-generate-report-from-sheet/SKILL.md +34 -0
- package/templates/claude/skills/recipe-label-and-archive-emails/SKILL.md +25 -0
- package/templates/claude/skills/recipe-log-deal-update/SKILL.md +25 -0
- package/templates/claude/skills/recipe-organize-drive-folder/SKILL.md +26 -0
- package/templates/claude/skills/recipe-plan-weekly-schedule/SKILL.md +26 -0
- package/templates/claude/skills/recipe-post-mortem-setup/SKILL.md +25 -0
- package/templates/claude/skills/recipe-reschedule-meeting/SKILL.md +25 -0
- package/templates/claude/skills/recipe-review-meet-participants/SKILL.md +25 -0
- package/templates/claude/skills/recipe-review-overdue-tasks/SKILL.md +25 -0
- package/templates/claude/skills/recipe-save-email-attachments/SKILL.md +26 -0
- package/templates/claude/skills/recipe-save-email-to-doc/SKILL.md +29 -0
- package/templates/claude/skills/recipe-schedule-recurring-event/SKILL.md +24 -0
- package/templates/claude/skills/recipe-send-team-announcement/SKILL.md +24 -0
- package/templates/claude/skills/recipe-share-doc-and-notify/SKILL.md +25 -0
- package/templates/claude/skills/recipe-share-event-materials/SKILL.md +25 -0
- package/templates/claude/skills/recipe-share-folder-with-team/SKILL.md +26 -0
- package/templates/claude/skills/recipe-sync-contacts-to-sheet/SKILL.md +25 -0
- package/templates/claude/skills/recipe-watch-drive-changes/SKILL.md +25 -0
- package/templates/mcp.json +12 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import matter from "gray-matter";
|
|
4
|
+
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
5
|
+
import { getAgentContent, getAgents } from "./agents";
|
|
6
|
+
import { isReadOnlyMcpTool } from "./approval-store";
|
|
7
|
+
import { buildCanUseTool, getAllowedToolsForRole } from "./role-permissions";
|
|
8
|
+
import { supabase } from "./supabase";
|
|
9
|
+
import type { Role } from "./auth-guard";
|
|
10
|
+
import type { AgentMeta } from "./types";
|
|
11
|
+
|
|
12
|
+
const PROJECT_ROOT = process.env.PROJECT_ROOT || "/mnt/c/Users/Admin/Documents/PersonalAIssistant";
|
|
13
|
+
|
|
14
|
+
// Read MCP servers from .mcp.json so any additions are picked up automatically
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
function getMcpServers(): Record<string, any> {
|
|
17
|
+
try {
|
|
18
|
+
const mcpPath = path.join(PROJECT_ROOT, ".mcp.json");
|
|
19
|
+
const raw = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
20
|
+
return raw.mcpServers || {};
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Read CLAUDE.md once for project context
|
|
27
|
+
function getProjectInstructions(): string {
|
|
28
|
+
try {
|
|
29
|
+
const claudeMd = path.join(PROJECT_ROOT, "CLAUDE.md");
|
|
30
|
+
return fs.readFileSync(claudeMd, "utf-8").trim();
|
|
31
|
+
} catch {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Build subagent definitions from all agent .md files (for the main assistant)
|
|
37
|
+
// AgentMcpServerSpec = string | Record<string, McpServerConfig> — SDK expects an array of these
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
function getSubagentDefinitions(excludeSlug?: string): Record<string, { description: string; prompt: string; tools: string[]; mcpServers?: Array<Record<string, any>> }> {
|
|
40
|
+
const agents = getAgents();
|
|
41
|
+
const allMcpServers = getMcpServers();
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
const defs: Record<string, { description: string; prompt: string; tools: string[]; mcpServers?: Array<Record<string, any>> }> = {};
|
|
44
|
+
|
|
45
|
+
for (const agent of agents) {
|
|
46
|
+
if (agent.slug === excludeSlug || agent.slug === "main") continue;
|
|
47
|
+
try {
|
|
48
|
+
const raw = fs.readFileSync(agent.filePath, "utf-8");
|
|
49
|
+
const { content } = matter(raw);
|
|
50
|
+
|
|
51
|
+
// Filter MCP servers to only those the agent has been granted access to
|
|
52
|
+
// Each element is { serverName: serverConfig } matching AgentMcpServerSpec format
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
let agentMcpServers: Array<Record<string, any>> | undefined;
|
|
55
|
+
if (agent.mcpServers && agent.mcpServers.length > 0) {
|
|
56
|
+
const matched = agent.mcpServers
|
|
57
|
+
.filter((name) => allMcpServers[name])
|
|
58
|
+
.map((name) => ({ [name]: allMcpServers[name] }));
|
|
59
|
+
if (matched.length > 0) {
|
|
60
|
+
agentMcpServers = matched;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
defs[agent.slug] = {
|
|
65
|
+
description: agent.description,
|
|
66
|
+
prompt: content.trim(),
|
|
67
|
+
tools: agent.tools.length > 0 ? agent.tools : ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
|
|
68
|
+
...(agentMcpServers ? { mcpServers: agentMcpServers } : {}),
|
|
69
|
+
};
|
|
70
|
+
} catch {
|
|
71
|
+
// skip agents we can't read
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return defs;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface AgentRunResult {
|
|
79
|
+
output: string;
|
|
80
|
+
costUsd: number;
|
|
81
|
+
durationMs: number;
|
|
82
|
+
sessionId: string;
|
|
83
|
+
aborted?: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface StreamCallbacks {
|
|
87
|
+
onToken?: (text: string) => void;
|
|
88
|
+
onToolCall?: (name: string, status: "start" | "end", detail?: { input?: unknown; result?: string }) => void;
|
|
89
|
+
onThinking?: (active: boolean) => void;
|
|
90
|
+
onProgress?: (info: string) => void;
|
|
91
|
+
onResult?: (result: AgentRunResult) => void;
|
|
92
|
+
onError?: (error: string) => void;
|
|
93
|
+
onDebug?: (msg: string) => void;
|
|
94
|
+
onSessionInit?: (sessionId: string) => void;
|
|
95
|
+
onApprovalRequired?: (toolName: string, toolInput: Record<string, unknown>, toolUseId: string) => Promise<boolean>;
|
|
96
|
+
signal?: AbortSignal;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function executeAgent(
|
|
100
|
+
agent: AgentMeta,
|
|
101
|
+
prompt: string,
|
|
102
|
+
callbacks?: StreamCallbacks,
|
|
103
|
+
resumeSessionId?: string,
|
|
104
|
+
userRole?: Role,
|
|
105
|
+
userEmail?: string
|
|
106
|
+
): Promise<AgentRunResult> {
|
|
107
|
+
const startTime = Date.now();
|
|
108
|
+
let output = "";
|
|
109
|
+
let sessionId = "";
|
|
110
|
+
let costUsd = 0;
|
|
111
|
+
let aborted = false;
|
|
112
|
+
// Track emitted tool starts to avoid duplicates when both stream_event
|
|
113
|
+
// and assistant message fire for the same tool call
|
|
114
|
+
let lastEmittedToolStart = "";
|
|
115
|
+
// Map tool_use_id → { name, input } so we can attach input/result to end events
|
|
116
|
+
const pendingTools = new Map<string, { name: string; input: unknown }>();
|
|
117
|
+
// Cap tool result text sent through SSE to avoid huge payloads
|
|
118
|
+
const TOOL_RESULT_MAX_CHARS = 8000;
|
|
119
|
+
|
|
120
|
+
const debug = (msg: string) => {
|
|
121
|
+
console.log(`[run-agent] ${msg}`);
|
|
122
|
+
callbacks?.onDebug?.(msg);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
debug(`Starting agent: ${agent.slug} (model: ${agent.model || "default"})`);
|
|
126
|
+
debug(`CWD: ${PROJECT_ROOT}`);
|
|
127
|
+
debug(`Prompt: ${prompt.slice(0, 100)}...`);
|
|
128
|
+
|
|
129
|
+
// Build system prompt: CLAUDE.md is always the base.
|
|
130
|
+
// For the "main" agent, CLAUDE.md IS the full system prompt and other agents become subagents.
|
|
131
|
+
// For specialist agents, append their markdown body after CLAUDE.md.
|
|
132
|
+
const isMainAgent = agent.slug === "main";
|
|
133
|
+
const projectInstructions = getProjectInstructions();
|
|
134
|
+
const agentBody = isMainAgent ? "" : getAgentContent(agent.slug);
|
|
135
|
+
|
|
136
|
+
// Fetch user preferences if we have an email
|
|
137
|
+
let userPreferences = "";
|
|
138
|
+
if (userEmail) {
|
|
139
|
+
try {
|
|
140
|
+
const { data } = await supabase
|
|
141
|
+
.from("user_preferences")
|
|
142
|
+
.select("preferences")
|
|
143
|
+
.eq("user_email", userEmail)
|
|
144
|
+
.single();
|
|
145
|
+
if (data?.preferences?.trim()) {
|
|
146
|
+
userPreferences = data.preferences.trim();
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
// No preferences found — that's fine
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const systemPrompt = [
|
|
154
|
+
projectInstructions,
|
|
155
|
+
agentBody,
|
|
156
|
+
userPreferences ? `## User Preferences\n\n${userPreferences}` : "",
|
|
157
|
+
].filter(Boolean).join("\n\n---\n\n");
|
|
158
|
+
|
|
159
|
+
// Load subagent definitions for the main agent
|
|
160
|
+
const subagents = isMainAgent ? getSubagentDefinitions() : undefined;
|
|
161
|
+
|
|
162
|
+
debug(`System prompt length: ${systemPrompt.length} chars`);
|
|
163
|
+
|
|
164
|
+
const stderrLines: string[] = [];
|
|
165
|
+
|
|
166
|
+
// Permission handler: auto-approve safe tools, require approval for write MCP tools
|
|
167
|
+
const hasApprovalHandler = !!callbacks?.onApprovalRequired;
|
|
168
|
+
const effectiveRole: Role = userRole || "admin";
|
|
169
|
+
|
|
170
|
+
// Admin gets full bypass — no permission prompts, no canUseTool restrictions.
|
|
171
|
+
// Operators get canUseTool with MCP write approval + path/command blocking.
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
173
|
+
const baseMcpCanUseTool = (hasApprovalHandler && effectiveRole !== "admin")
|
|
174
|
+
? async (
|
|
175
|
+
toolName: string,
|
|
176
|
+
input: Record<string, unknown>,
|
|
177
|
+
options: { signal: AbortSignal; toolUseID: string }
|
|
178
|
+
) => {
|
|
179
|
+
// Auto-approve non-MCP tools (built-in: Bash, Read, Write, Edit, Glob, Grep, etc.)
|
|
180
|
+
if (!toolName.startsWith("mcp__")) {
|
|
181
|
+
return { behavior: "allow" as const };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Auto-approve read-only MCP tools
|
|
185
|
+
if (isReadOnlyMcpTool(toolName)) {
|
|
186
|
+
debug(`Auto-approving read-only MCP tool: ${toolName}`);
|
|
187
|
+
return { behavior: "allow" as const };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Write MCP tool — ask user
|
|
191
|
+
debug(`Requesting approval for write MCP tool: ${toolName}`);
|
|
192
|
+
try {
|
|
193
|
+
const approved = await callbacks!.onApprovalRequired!(toolName, input, options.toolUseID);
|
|
194
|
+
if (approved) {
|
|
195
|
+
debug(`User approved: ${toolName}`);
|
|
196
|
+
return { behavior: "allow" as const };
|
|
197
|
+
} else {
|
|
198
|
+
debug(`User denied: ${toolName}`);
|
|
199
|
+
return { behavior: "deny" as const, message: "User denied this action" };
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
debug(`Approval error/cancelled for: ${toolName}`);
|
|
203
|
+
return { behavior: "deny" as const, message: "Approval cancelled" };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
: undefined;
|
|
207
|
+
|
|
208
|
+
// Wrap with role-based restrictions (operators get path/command blocking)
|
|
209
|
+
const canUseTool = buildCanUseTool(effectiveRole, baseMcpCanUseTool);
|
|
210
|
+
|
|
211
|
+
// Determine permission mode:
|
|
212
|
+
// - admin → always bypassPermissions (full trust, no prompts)
|
|
213
|
+
// - operator → "default" + canUseTool (MCP approval + path/command blocking)
|
|
214
|
+
// - viewer → restricted allowedTools, no canUseTool
|
|
215
|
+
const useBypass = effectiveRole === "admin";
|
|
216
|
+
const allowedTools = getAllowedToolsForRole(effectiveRole, agent.tools);
|
|
217
|
+
|
|
218
|
+
debug(`Role: ${effectiveRole}, bypass: ${useBypass}, tools: ${allowedTools.length}`);
|
|
219
|
+
|
|
220
|
+
const q = query({
|
|
221
|
+
prompt,
|
|
222
|
+
options: {
|
|
223
|
+
...(resumeSessionId ? { resume: resumeSessionId } : {}),
|
|
224
|
+
cwd: PROJECT_ROOT,
|
|
225
|
+
env: {
|
|
226
|
+
...process.env,
|
|
227
|
+
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY || "",
|
|
228
|
+
},
|
|
229
|
+
...(useBypass
|
|
230
|
+
? { permissionMode: "bypassPermissions" as const, allowDangerouslySkipPermissions: true }
|
|
231
|
+
: { permissionMode: "default" as const, ...(canUseTool ? { canUseTool } : {}) }),
|
|
232
|
+
systemPrompt,
|
|
233
|
+
maxBudgetUsd: isMainAgent ? 5.0 : 2.0,
|
|
234
|
+
model: agent.model || undefined,
|
|
235
|
+
mcpServers: getMcpServers(),
|
|
236
|
+
...(subagents ? { agents: subagents } : {}),
|
|
237
|
+
allowedTools,
|
|
238
|
+
includePartialMessages: true,
|
|
239
|
+
debug: true,
|
|
240
|
+
stderr: (data: string) => {
|
|
241
|
+
stderrLines.push(data);
|
|
242
|
+
debug(`[stderr] ${data.trim()}`);
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
for await (const message of q) {
|
|
249
|
+
if (callbacks?.signal?.aborted) {
|
|
250
|
+
debug("Aborted by signal");
|
|
251
|
+
aborted = true;
|
|
252
|
+
q.close();
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
debug(`Message type: ${message.type}${("subtype" in message) ? ` subtype: ${(message as { subtype?: string }).subtype}` : ""}`);
|
|
257
|
+
|
|
258
|
+
if (message.type === "system" && "subtype" in message && message.subtype === "init") {
|
|
259
|
+
sessionId = message.session_id;
|
|
260
|
+
debug(`Session initialized: ${sessionId}`);
|
|
261
|
+
callbacks?.onSessionInit?.(sessionId);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Stream partial text tokens and real-time tool/thinking detection
|
|
265
|
+
if (message.type === "stream_event" && "event" in message) {
|
|
266
|
+
const event = message.event as {
|
|
267
|
+
type: string;
|
|
268
|
+
index?: number;
|
|
269
|
+
delta?: { type: string; text?: string };
|
|
270
|
+
content_block?: { type: string; name?: string };
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Real-time text streaming
|
|
274
|
+
if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta.text) {
|
|
275
|
+
callbacks?.onToken?.(event.delta.text);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Real-time tool call start (fires as soon as the model starts a tool_use block)
|
|
279
|
+
if (event.type === "content_block_start" && event.content_block?.type === "tool_use") {
|
|
280
|
+
const toolName = event.content_block.name || "unknown";
|
|
281
|
+
lastEmittedToolStart = toolName;
|
|
282
|
+
debug(` [stream] Tool use started: ${toolName}`);
|
|
283
|
+
callbacks?.onToolCall?.(toolName, "start");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Real-time thinking start
|
|
287
|
+
if (event.type === "content_block_start" && event.content_block?.type === "thinking") {
|
|
288
|
+
debug(` [stream] Thinking started`);
|
|
289
|
+
callbacks?.onThinking?.(true);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Content block finished — end thinking/tool state
|
|
293
|
+
if (event.type === "content_block_stop") {
|
|
294
|
+
callbacks?.onThinking?.(false);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Collect full assistant messages and emit tool/thinking events
|
|
299
|
+
if (message.type === "assistant" && "message" in message) {
|
|
300
|
+
const msg = message.message as { content?: Array<{ type: string; text?: string; name?: string; input?: unknown }> ; stop_reason?: string; error?: unknown };
|
|
301
|
+
debug(`Assistant message: stop_reason=${msg.stop_reason}, content_blocks=${msg.content?.length || 0}, error=${JSON.stringify(msg.error || null)}`);
|
|
302
|
+
if (msg.content) {
|
|
303
|
+
for (const block of msg.content) {
|
|
304
|
+
const b = block as { type: string; text?: string; name?: string; input?: unknown; id?: string };
|
|
305
|
+
debug(` Block: type=${b.type}${b.name ? ` name=${b.name}` : ""}${b.text ? ` text=${b.text.slice(0, 200)}` : ""}`);
|
|
306
|
+
if (b.type === "text" && b.text) {
|
|
307
|
+
output += b.text;
|
|
308
|
+
}
|
|
309
|
+
// Emit tool call start from assistant messages as a fallback
|
|
310
|
+
// (stream_events may not fire for subagent tool calls)
|
|
311
|
+
if (b.type === "tool_use" && b.name) {
|
|
312
|
+
// Serialize input and cap if huge (e.g., Write tool with large content)
|
|
313
|
+
const inputStr = JSON.stringify(b.input);
|
|
314
|
+
const cappedInput = inputStr.length > TOOL_RESULT_MAX_CHARS
|
|
315
|
+
? JSON.parse(JSON.stringify(b.input, (_k, v) =>
|
|
316
|
+
typeof v === "string" && v.length > 2000
|
|
317
|
+
? v.slice(0, 2000) + `… [${v.length.toLocaleString()} chars]`
|
|
318
|
+
: v
|
|
319
|
+
))
|
|
320
|
+
: b.input;
|
|
321
|
+
|
|
322
|
+
// Track the tool input for later pairing with result
|
|
323
|
+
if (b.id) {
|
|
324
|
+
pendingTools.set(b.id, { name: b.name, input: cappedInput });
|
|
325
|
+
}
|
|
326
|
+
// Skip if already emitted via stream_event for this exact tool
|
|
327
|
+
if (lastEmittedToolStart === b.name) {
|
|
328
|
+
lastEmittedToolStart = "";
|
|
329
|
+
} else {
|
|
330
|
+
callbacks?.onToolCall?.(b.name, "start", { input: cappedInput });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Tool results (user messages with tool_result) signal tool calls ended
|
|
338
|
+
if (message.type === "user" && "message" in message) {
|
|
339
|
+
const userMsg = message.message as { content?: Array<{ type: string; tool_use_id?: string; content?: unknown }> };
|
|
340
|
+
if (userMsg.content) {
|
|
341
|
+
for (const block of userMsg.content) {
|
|
342
|
+
if (block.type === "tool_result") {
|
|
343
|
+
// Extract text from tool_result content
|
|
344
|
+
let resultText = "";
|
|
345
|
+
const rc = block.content;
|
|
346
|
+
if (typeof rc === "string") {
|
|
347
|
+
resultText = rc;
|
|
348
|
+
} else if (Array.isArray(rc)) {
|
|
349
|
+
resultText = (rc as Array<{ type: string; text?: string }>)
|
|
350
|
+
.filter((c) => c.type === "text" && c.text)
|
|
351
|
+
.map((c) => c.text)
|
|
352
|
+
.join("\n");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Look up the pending tool to get its name and input
|
|
356
|
+
const pending = block.tool_use_id ? pendingTools.get(block.tool_use_id) : undefined;
|
|
357
|
+
const toolName = pending?.name || "";
|
|
358
|
+
const toolInput = pending?.input;
|
|
359
|
+
if (block.tool_use_id) pendingTools.delete(block.tool_use_id);
|
|
360
|
+
|
|
361
|
+
// Truncate large results to keep SSE payloads manageable
|
|
362
|
+
const truncated = resultText.length > TOOL_RESULT_MAX_CHARS;
|
|
363
|
+
const cappedResult = truncated
|
|
364
|
+
? resultText.slice(0, TOOL_RESULT_MAX_CHARS) + `\n\n… [truncated — ${resultText.length.toLocaleString()} chars total]`
|
|
365
|
+
: resultText;
|
|
366
|
+
|
|
367
|
+
callbacks?.onToolCall?.(toolName, "end", {
|
|
368
|
+
input: toolInput,
|
|
369
|
+
result: cappedResult,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Task progress from subagents
|
|
377
|
+
if (message.type === "system" && "subtype" in message) {
|
|
378
|
+
const sub = (message as { subtype?: string }).subtype;
|
|
379
|
+
if (sub === "task_started") {
|
|
380
|
+
callbacks?.onProgress?.("Subagent started");
|
|
381
|
+
} else if (sub === "task_notification") {
|
|
382
|
+
callbacks?.onProgress?.("Subagent completed");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Final result
|
|
387
|
+
if (message.type === "result") {
|
|
388
|
+
// Log the full result for debugging
|
|
389
|
+
const resultJson = JSON.stringify(message, null, 2);
|
|
390
|
+
debug(`Full result message:\n${resultJson.slice(0, 2000)}`);
|
|
391
|
+
|
|
392
|
+
const result = message as {
|
|
393
|
+
total_cost_usd: number;
|
|
394
|
+
duration_ms: number;
|
|
395
|
+
session_id: string;
|
|
396
|
+
result?: string;
|
|
397
|
+
is_error: boolean;
|
|
398
|
+
subtype: string;
|
|
399
|
+
errors?: string[];
|
|
400
|
+
};
|
|
401
|
+
costUsd = result.total_cost_usd;
|
|
402
|
+
sessionId = result.session_id || sessionId;
|
|
403
|
+
|
|
404
|
+
debug(`Result: subtype=${result.subtype}, cost=$${result.total_cost_usd}, is_error=${result.is_error}`);
|
|
405
|
+
if (result.errors) {
|
|
406
|
+
debug(`Errors: ${JSON.stringify(result.errors)}`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Check is_error regardless of subtype
|
|
410
|
+
if (result.is_error) {
|
|
411
|
+
const errMsg = result.errors?.join("; ") || result.result || `Agent ended with error (subtype: ${result.subtype})`;
|
|
412
|
+
callbacks?.onError?.(errMsg);
|
|
413
|
+
throw new Error(errMsg);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (result.result) {
|
|
417
|
+
output = result.result;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
} catch (err) {
|
|
422
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
423
|
+
debug(`Error caught: ${errMsg}`);
|
|
424
|
+
if (stderrLines.length > 0) {
|
|
425
|
+
debug(`Stderr output (last 20 lines):\n${stderrLines.slice(-20).join("\n")}`);
|
|
426
|
+
}
|
|
427
|
+
callbacks?.onError?.(errMsg);
|
|
428
|
+
throw err;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const runResult: AgentRunResult = {
|
|
432
|
+
output,
|
|
433
|
+
costUsd,
|
|
434
|
+
durationMs: Date.now() - startTime,
|
|
435
|
+
sessionId,
|
|
436
|
+
aborted,
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
debug(`Completed: cost=$${costUsd.toFixed(4)}, duration=${runResult.durationMs}ms, aborted=${aborted}`);
|
|
440
|
+
callbacks?.onResult?.(runResult);
|
|
441
|
+
return runResult;
|
|
442
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createServerClient } from "@supabase/ssr";
|
|
2
|
+
import { NextResponse, type NextRequest } from "next/server";
|
|
3
|
+
|
|
4
|
+
export async function updateSession(request: NextRequest) {
|
|
5
|
+
let supabaseResponse = NextResponse.next({ request });
|
|
6
|
+
|
|
7
|
+
const supabase = createServerClient(
|
|
8
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
+
{
|
|
11
|
+
cookies: {
|
|
12
|
+
getAll() {
|
|
13
|
+
return request.cookies.getAll();
|
|
14
|
+
},
|
|
15
|
+
setAll(cookiesToSet) {
|
|
16
|
+
cookiesToSet.forEach(({ name, value }) =>
|
|
17
|
+
request.cookies.set(name, value)
|
|
18
|
+
);
|
|
19
|
+
supabaseResponse = NextResponse.next({ request });
|
|
20
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
21
|
+
supabaseResponse.cookies.set(name, value, options)
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Refresh session — do NOT remove this
|
|
29
|
+
const {
|
|
30
|
+
data: { user },
|
|
31
|
+
} = await supabase.auth.getUser();
|
|
32
|
+
|
|
33
|
+
const isLoginPage = request.nextUrl.pathname.startsWith("/login");
|
|
34
|
+
const isAuthCallback = request.nextUrl.pathname.startsWith("/auth");
|
|
35
|
+
const isApi = request.nextUrl.pathname.startsWith("/api");
|
|
36
|
+
const isNotAuthorized = request.nextUrl.pathname === "/not-authorized";
|
|
37
|
+
|
|
38
|
+
// Redirect unauthenticated users to login (except login page and auth callback)
|
|
39
|
+
if (!user && !isLoginPage && !isAuthCallback && !isApi) {
|
|
40
|
+
const url = request.nextUrl.clone();
|
|
41
|
+
url.pathname = "/login";
|
|
42
|
+
return NextResponse.redirect(url);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check if authenticated user is in the allowed user_roles table
|
|
46
|
+
if (user && !isLoginPage && !isAuthCallback && !isNotAuthorized) {
|
|
47
|
+
const { data: role } = await supabase.rpc("check_user_role", {
|
|
48
|
+
user_email: user.email,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!role) {
|
|
52
|
+
// User is authenticated but not authorized — sign them out and show error
|
|
53
|
+
if (isApi) {
|
|
54
|
+
return NextResponse.json({ error: "Not authorized" }, { status: 403 });
|
|
55
|
+
}
|
|
56
|
+
const url = request.nextUrl.clone();
|
|
57
|
+
url.pathname = "/not-authorized";
|
|
58
|
+
return NextResponse.redirect(url);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return supabaseResponse;
|
|
63
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createServerClient } from "@supabase/ssr";
|
|
2
|
+
import { cookies } from "next/headers";
|
|
3
|
+
|
|
4
|
+
export async function createClient() {
|
|
5
|
+
const cookieStore = await cookies();
|
|
6
|
+
|
|
7
|
+
return createServerClient(
|
|
8
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
+
{
|
|
11
|
+
cookies: {
|
|
12
|
+
getAll() {
|
|
13
|
+
return cookieStore.getAll();
|
|
14
|
+
},
|
|
15
|
+
setAll(cookiesToSet) {
|
|
16
|
+
try {
|
|
17
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
+
cookieStore.set(name, value, options)
|
|
19
|
+
);
|
|
20
|
+
} catch {
|
|
21
|
+
// Ignored — called from Server Component where cookies can't be set.
|
|
22
|
+
// Middleware will refresh the session instead.
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createClient } from "@supabase/supabase-js";
|
|
2
|
+
|
|
3
|
+
const supabaseUrl = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
|
4
|
+
const supabaseKey = process.env.SUPABASE_ANON_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
|
5
|
+
|
|
6
|
+
export const supabase = createClient(supabaseUrl, supabaseKey);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Human-friendly descriptions for technical tool names, shared across all UI surfaces */
|
|
2
|
+
export const TOOL_DESCRIPTIONS: Record<string, string> = {
|
|
3
|
+
Bash: "Runs a terminal command on the system",
|
|
4
|
+
Glob: "Searches for files by name or pattern",
|
|
5
|
+
Grep: "Searches inside files for specific text",
|
|
6
|
+
Read: "Reads the contents of a file",
|
|
7
|
+
Write: "Creates or overwrites a file",
|
|
8
|
+
Edit: "Makes targeted edits to a file",
|
|
9
|
+
WebSearch: "Searches the web for information",
|
|
10
|
+
WebFetch: "Fetches content from a web page",
|
|
11
|
+
Agent: "Delegates a task to a specialized sub-agent",
|
|
12
|
+
Task: "Manages background tasks",
|
|
13
|
+
NotebookEdit: "Edits a Jupyter notebook cell",
|
|
14
|
+
AskUserQuestion: "Asks you a question before continuing",
|
|
15
|
+
TodoWrite: "Updates the to-do list",
|
|
16
|
+
Mcp: "Calls an external integration tool",
|
|
17
|
+
ListMcpResources: "Lists available integration resources",
|
|
18
|
+
ReadMcpResource: "Reads data from an integration resource",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function getToolDescription(name: string): string | undefined {
|
|
22
|
+
if (TOOL_DESCRIPTIONS[name]) return TOOL_DESCRIPTIONS[name];
|
|
23
|
+
if (name.startsWith("mcp__")) {
|
|
24
|
+
const parts = name.replace(/^mcp__/, "").split("__");
|
|
25
|
+
const service = (parts[0] || "").replace(/^claude_ai_/, "");
|
|
26
|
+
return `Action provided by the ${service} integration`;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export interface AgentMeta {
|
|
2
|
+
slug: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
tools: string[];
|
|
6
|
+
mcpServers?: string[];
|
|
7
|
+
color?: string;
|
|
8
|
+
emoji?: string;
|
|
9
|
+
vibe?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AgentRun {
|
|
15
|
+
id: string;
|
|
16
|
+
agent_slug: string;
|
|
17
|
+
agent_name: string;
|
|
18
|
+
prompt: string;
|
|
19
|
+
output: string | null;
|
|
20
|
+
status: "queued" | "running" | "completed" | "failed" | "stopped";
|
|
21
|
+
cost_usd: number | null;
|
|
22
|
+
duration_ms: number | null;
|
|
23
|
+
session_id: string | null;
|
|
24
|
+
error: string | null;
|
|
25
|
+
metadata: Record<string, unknown>;
|
|
26
|
+
pending_approval: {
|
|
27
|
+
tool_use_id: string;
|
|
28
|
+
tool_name: string;
|
|
29
|
+
tool_input: Record<string, unknown>;
|
|
30
|
+
} | null;
|
|
31
|
+
schedule_id: string | null;
|
|
32
|
+
event_count: number;
|
|
33
|
+
created_at: string;
|
|
34
|
+
started_at: string | null;
|
|
35
|
+
completed_at: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface RunEvent {
|
|
39
|
+
id: number;
|
|
40
|
+
run_id: string;
|
|
41
|
+
seq: number;
|
|
42
|
+
event_type: string;
|
|
43
|
+
payload: Record<string, unknown>;
|
|
44
|
+
created_at: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AgentSchedule {
|
|
48
|
+
id: string;
|
|
49
|
+
agent_slug: string;
|
|
50
|
+
agent_name: string;
|
|
51
|
+
prompt: string;
|
|
52
|
+
cron: string;
|
|
53
|
+
skill_slug: string | null;
|
|
54
|
+
enabled: boolean;
|
|
55
|
+
last_run_at: string | null;
|
|
56
|
+
next_run_at: string | null;
|
|
57
|
+
metadata: Record<string, unknown>;
|
|
58
|
+
created_at: string;
|
|
59
|
+
updated_at: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface Notification {
|
|
63
|
+
id: string;
|
|
64
|
+
run_id: string | null;
|
|
65
|
+
schedule_id: string | null;
|
|
66
|
+
agent_slug: string;
|
|
67
|
+
session_id: string | null;
|
|
68
|
+
type: "completed" | "failed" | "needs_review" | "approval_needed";
|
|
69
|
+
title: string;
|
|
70
|
+
summary: string | null;
|
|
71
|
+
read: boolean;
|
|
72
|
+
created_at: string;
|
|
73
|
+
}
|