stagent 0.5.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 +8 -8
- package/dist/cli.js +146 -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 +33 -56
- 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 +70 -40
- package/docs/features/settings.md +74 -46
- 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 +77 -45
- package/docs/superpowers/plans/2026-03-30-finish-in-progress-features.md +547 -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 +10 -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/route.ts +15 -0
- package/src/app/api/chat/entities/search/route.ts +46 -31
- 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/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/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 +10 -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 +22 -19
- package/src/components/chat/chat-input.tsx +5 -0
- package/src/components/chat/chat-model-selector.tsx +42 -1
- package/src/components/chat/chat-shell.tsx +2 -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/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/schedules/schedule-create-sheet.tsx +9 -1
- package/src/components/schedules/schedule-form.tsx +348 -9
- package/src/components/schedules/schedule-list.tsx +15 -2
- package/src/components/settings/auth-method-selector.tsx +7 -1
- 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/web-search-section.tsx +101 -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-chat-autocomplete.ts +24 -26
- 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__/claude-agent.test.ts +3 -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 +59 -4
- package/src/lib/agents/claude-agent.ts +26 -199
- 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/context-builder.ts +22 -2
- package/src/lib/chat/engine.ts +95 -13
- package/src/lib/chat/ollama-engine.ts +198 -0
- package/src/lib/chat/stagent-tools.ts +106 -20
- package/src/lib/chat/tool-catalog.ts +24 -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 +7 -7
- 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 +11 -5
- package/src/lib/constants/known-tools.ts +19 -0
- package/src/lib/constants/prose-styles.ts +1 -1
- package/src/lib/constants/settings.ts +7 -0
- package/src/lib/data/channel-bindings.ts +85 -0
- package/src/lib/data/clear.ts +22 -0
- 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 +162 -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 +187 -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/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/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
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama chat engine — streams messages via the Ollama /api/chat endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Follows the same ChatStreamEvent protocol as the main engine
|
|
5
|
+
* so the chat UI can render Ollama responses identically.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { db } from "@/lib/db";
|
|
9
|
+
import { chatMessages, projects } from "@/lib/db/schema";
|
|
10
|
+
import { eq } from "drizzle-orm";
|
|
11
|
+
import { getSetting } from "@/lib/settings/helpers";
|
|
12
|
+
import { SETTINGS_KEYS } from "@/lib/constants/settings";
|
|
13
|
+
import {
|
|
14
|
+
getConversation,
|
|
15
|
+
addMessage,
|
|
16
|
+
updateMessageStatus,
|
|
17
|
+
updateMessageContent,
|
|
18
|
+
} from "@/lib/data/chat";
|
|
19
|
+
import { buildChatContext } from "./context-builder";
|
|
20
|
+
import { getWorkspaceContext } from "@/lib/environment/workspace-context";
|
|
21
|
+
import type { ChatStreamEvent } from "./types";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Send a user message to Ollama and stream the response.
|
|
25
|
+
*/
|
|
26
|
+
export async function* sendOllamaMessage(
|
|
27
|
+
conversationId: string,
|
|
28
|
+
userContent: string,
|
|
29
|
+
signal?: AbortSignal
|
|
30
|
+
): AsyncGenerator<ChatStreamEvent> {
|
|
31
|
+
const conversation = await getConversation(conversationId);
|
|
32
|
+
if (!conversation) {
|
|
33
|
+
yield { type: "error", message: "Conversation not found" };
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
yield { type: "status", phase: "preparing", message: "Connecting to Ollama..." };
|
|
38
|
+
|
|
39
|
+
// Resolve Ollama base URL and model
|
|
40
|
+
const baseUrl =
|
|
41
|
+
(await getSetting(SETTINGS_KEYS.OLLAMA_BASE_URL)) || "http://localhost:11434";
|
|
42
|
+
const modelId =
|
|
43
|
+
conversation.modelId?.replace(/^ollama:/, "") ||
|
|
44
|
+
(await getSetting(SETTINGS_KEYS.OLLAMA_DEFAULT_MODEL)) ||
|
|
45
|
+
"llama3.2";
|
|
46
|
+
|
|
47
|
+
// Build context
|
|
48
|
+
let projectName: string | null = null;
|
|
49
|
+
let projectCwd: string | null = null;
|
|
50
|
+
if (conversation.projectId) {
|
|
51
|
+
const project = db
|
|
52
|
+
.select()
|
|
53
|
+
.from(projects)
|
|
54
|
+
.where(eq(projects.id, conversation.projectId))
|
|
55
|
+
.get();
|
|
56
|
+
if (project) {
|
|
57
|
+
projectName = project.name;
|
|
58
|
+
projectCwd = project.workingDirectory ?? null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const workspace = getWorkspaceContext();
|
|
63
|
+
if (projectCwd) workspace.cwd = projectCwd;
|
|
64
|
+
|
|
65
|
+
const context = await buildChatContext({
|
|
66
|
+
conversationId,
|
|
67
|
+
projectId: conversation.projectId,
|
|
68
|
+
projectName,
|
|
69
|
+
workspace,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Persist user message
|
|
73
|
+
await addMessage({
|
|
74
|
+
conversationId,
|
|
75
|
+
role: "user",
|
|
76
|
+
content: userContent,
|
|
77
|
+
status: "complete",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Create assistant message placeholder
|
|
81
|
+
const assistantMsg = await addMessage({
|
|
82
|
+
conversationId,
|
|
83
|
+
role: "assistant",
|
|
84
|
+
content: "",
|
|
85
|
+
status: "streaming",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Build message history for Ollama
|
|
89
|
+
const history = db
|
|
90
|
+
.select()
|
|
91
|
+
.from(chatMessages)
|
|
92
|
+
.where(eq(chatMessages.conversationId, conversationId))
|
|
93
|
+
.orderBy(chatMessages.createdAt)
|
|
94
|
+
.all();
|
|
95
|
+
|
|
96
|
+
const messages = [
|
|
97
|
+
// System prompt from context
|
|
98
|
+
...(context.systemPrompt
|
|
99
|
+
? [{ role: "system" as const, content: context.systemPrompt }]
|
|
100
|
+
: []),
|
|
101
|
+
// Conversation history (exclude the placeholder assistant msg)
|
|
102
|
+
...history
|
|
103
|
+
.filter((m) => m.id !== assistantMsg.id && m.content)
|
|
104
|
+
.map((m) => ({
|
|
105
|
+
role: m.role as "user" | "assistant" | "system",
|
|
106
|
+
content: m.content!,
|
|
107
|
+
})),
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
// Stream from Ollama
|
|
111
|
+
let accumulated = "";
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch(`${baseUrl}/api/chat`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: { "Content-Type": "application/json" },
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
model: modelId,
|
|
118
|
+
messages,
|
|
119
|
+
stream: true,
|
|
120
|
+
}),
|
|
121
|
+
signal,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
126
|
+
yield { type: "error", message: `Ollama error (${response.status}): ${errorText}` };
|
|
127
|
+
await updateMessageStatus(assistantMsg.id, "complete");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const reader = response.body?.getReader();
|
|
132
|
+
if (!reader) {
|
|
133
|
+
yield { type: "error", message: "No response stream from Ollama" };
|
|
134
|
+
await updateMessageStatus(assistantMsg.id, "complete");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
yield { type: "status", phase: "streaming", message: "Streaming response..." };
|
|
139
|
+
|
|
140
|
+
const decoder = new TextDecoder();
|
|
141
|
+
let buffer = "";
|
|
142
|
+
|
|
143
|
+
while (true) {
|
|
144
|
+
const { done, value } = await reader.read();
|
|
145
|
+
if (done) break;
|
|
146
|
+
|
|
147
|
+
buffer += decoder.decode(value, { stream: true });
|
|
148
|
+
const lines = buffer.split("\n");
|
|
149
|
+
buffer = lines.pop() ?? "";
|
|
150
|
+
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
if (!line.trim()) continue;
|
|
153
|
+
try {
|
|
154
|
+
const parsed = JSON.parse(line);
|
|
155
|
+
const delta = parsed.message?.content ?? "";
|
|
156
|
+
if (delta) {
|
|
157
|
+
accumulated += delta;
|
|
158
|
+
yield { type: "delta", content: delta };
|
|
159
|
+
}
|
|
160
|
+
if (parsed.done) break;
|
|
161
|
+
} catch {
|
|
162
|
+
// Skip malformed lines
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Process any remaining buffer
|
|
168
|
+
if (buffer.trim()) {
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JSON.parse(buffer);
|
|
171
|
+
const delta = parsed.message?.content ?? "";
|
|
172
|
+
if (delta) {
|
|
173
|
+
accumulated += delta;
|
|
174
|
+
yield { type: "delta", content: delta };
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
// ignore
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Persist the complete response
|
|
182
|
+
await updateMessageContent(assistantMsg.id, accumulated);
|
|
183
|
+
await updateMessageStatus(assistantMsg.id, "complete");
|
|
184
|
+
|
|
185
|
+
yield { type: "done", messageId: assistantMsg.id, quickAccess: [] };
|
|
186
|
+
} catch (err) {
|
|
187
|
+
if (signal?.aborted) {
|
|
188
|
+
yield { type: "error", message: "Request cancelled" };
|
|
189
|
+
} else {
|
|
190
|
+
const msg = err instanceof Error ? err.message : "Ollama streaming failed";
|
|
191
|
+
yield { type: "error", message: msg };
|
|
192
|
+
}
|
|
193
|
+
if (accumulated) {
|
|
194
|
+
await updateMessageContent(assistantMsg.id, accumulated);
|
|
195
|
+
}
|
|
196
|
+
await updateMessageStatus(assistantMsg.id, "complete");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
1
|
+
import { tool as sdkTool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import { type ToolContext } from "./tools/helpers";
|
|
3
|
+
import {
|
|
4
|
+
type ToolDefinition,
|
|
5
|
+
type ToolResult,
|
|
6
|
+
toAnthropicToolDef,
|
|
7
|
+
toOpenAIFunctionDef,
|
|
8
|
+
type AnthropicToolDef,
|
|
9
|
+
type OpenAIFunctionDef,
|
|
10
|
+
} from "./tool-registry";
|
|
3
11
|
import { projectTools } from "./tools/project-tools";
|
|
4
12
|
import { taskTools } from "./tools/task-tools";
|
|
5
13
|
import { workflowTools } from "./tools/workflow-tools";
|
|
@@ -10,6 +18,98 @@ import { profileTools } from "./tools/profile-tools";
|
|
|
10
18
|
import { usageTools } from "./tools/usage-tools";
|
|
11
19
|
import { settingsTools } from "./tools/settings-tools";
|
|
12
20
|
import { chatHistoryTools } from "./tools/chat-history-tools";
|
|
21
|
+
import { handoffTools } from "./tools/handoff-tools";
|
|
22
|
+
|
|
23
|
+
// ── Tool server types ────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export interface ProviderToolKit {
|
|
26
|
+
tools: AnthropicToolDef[] | OpenAIFunctionDef[];
|
|
27
|
+
/** Execute a tool handler by name. Throws if tool not found. */
|
|
28
|
+
executeHandler(name: string, args: Record<string, unknown>): Promise<ToolResult>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ToolServer {
|
|
32
|
+
/** Backward-compatible SDK MCP server for the chat engine. */
|
|
33
|
+
asMcpServer(): ReturnType<typeof createSdkMcpServer>;
|
|
34
|
+
/** Provider-formatted tool arrays + handler lookup for direct API runtimes. */
|
|
35
|
+
forProvider(provider: "anthropic" | "openai"): ProviderToolKit;
|
|
36
|
+
/** Raw tool definitions for inspection / testing. */
|
|
37
|
+
readonly definitions: ToolDefinition[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Factory ──────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
function collectAllTools(ctx: ToolContext): ToolDefinition[] {
|
|
43
|
+
return [
|
|
44
|
+
...projectTools(ctx),
|
|
45
|
+
...taskTools(ctx),
|
|
46
|
+
...workflowTools(ctx),
|
|
47
|
+
...scheduleTools(ctx),
|
|
48
|
+
...documentTools(ctx),
|
|
49
|
+
...notificationTools(ctx),
|
|
50
|
+
...profileTools(ctx),
|
|
51
|
+
...usageTools(ctx),
|
|
52
|
+
...settingsTools(ctx),
|
|
53
|
+
...chatHistoryTools(ctx),
|
|
54
|
+
...handoffTools(ctx),
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create a tool server that supports both SDK MCP mode and direct API mode.
|
|
60
|
+
*
|
|
61
|
+
* - `.asMcpServer()` re-wraps definitions into SDK `tool()` calls for
|
|
62
|
+
* backward compatibility with the chat engine.
|
|
63
|
+
* - `.forProvider("anthropic" | "openai")` returns provider-formatted
|
|
64
|
+
* tool definitions + a handler lookup for direct API runtimes.
|
|
65
|
+
*/
|
|
66
|
+
export function createToolServer(
|
|
67
|
+
projectId?: string | null,
|
|
68
|
+
onToolResult?: (toolName: string, result: unknown) => void,
|
|
69
|
+
): ToolServer {
|
|
70
|
+
const ctx: ToolContext = { projectId, onToolResult };
|
|
71
|
+
const allTools = collectAllTools(ctx);
|
|
72
|
+
|
|
73
|
+
// Handler lookup map (built once, shared across modes)
|
|
74
|
+
const handlerMap = new Map<string, ToolDefinition["handler"]>(
|
|
75
|
+
allTools.map((t) => [t.name, t.handler]),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
async function executeHandler(
|
|
79
|
+
name: string,
|
|
80
|
+
args: Record<string, unknown>,
|
|
81
|
+
): Promise<ToolResult> {
|
|
82
|
+
const handler = handlerMap.get(name);
|
|
83
|
+
if (!handler) throw new Error(`Unknown tool: ${name}`);
|
|
84
|
+
return handler(args);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
asMcpServer() {
|
|
89
|
+
// Re-wrap ToolDefinitions into SDK tool() format
|
|
90
|
+
const sdkTools = allTools.map((def) =>
|
|
91
|
+
sdkTool(def.name, def.description, def.zodShape, def.handler),
|
|
92
|
+
);
|
|
93
|
+
return createSdkMcpServer({
|
|
94
|
+
name: "stagent",
|
|
95
|
+
version: "1.0.0",
|
|
96
|
+
tools: sdkTools,
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
forProvider(provider) {
|
|
101
|
+
const tools =
|
|
102
|
+
provider === "anthropic"
|
|
103
|
+
? allTools.map(toAnthropicToolDef)
|
|
104
|
+
: allTools.map(toOpenAIFunctionDef);
|
|
105
|
+
return { tools, executeHandler };
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
definitions: allTools,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Backward-compatible export ───────────────────────────────────────
|
|
13
113
|
|
|
14
114
|
/**
|
|
15
115
|
* Create an in-process MCP server exposing all Stagent tools.
|
|
@@ -17,27 +117,13 @@ import { chatHistoryTools } from "./tools/chat-history-tools";
|
|
|
17
117
|
* `onToolResult` is called after each successful CRUD operation with the
|
|
18
118
|
* tool name and returned entity data — used by the entity detector to
|
|
19
119
|
* generate deterministic Quick Access navigation links.
|
|
120
|
+
*
|
|
121
|
+
* @deprecated Use `createToolServer()` for new code. This wrapper exists
|
|
122
|
+
* for backward compatibility with the chat engine.
|
|
20
123
|
*/
|
|
21
124
|
export function createStagentMcpServer(
|
|
22
125
|
projectId?: string | null,
|
|
23
|
-
onToolResult?: (toolName: string, result: unknown) => void
|
|
126
|
+
onToolResult?: (toolName: string, result: unknown) => void,
|
|
24
127
|
) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return createSdkMcpServer({
|
|
28
|
-
name: "stagent",
|
|
29
|
-
version: "1.0.0",
|
|
30
|
-
tools: [
|
|
31
|
-
...projectTools(ctx),
|
|
32
|
-
...taskTools(ctx),
|
|
33
|
-
...workflowTools(ctx),
|
|
34
|
-
...scheduleTools(ctx),
|
|
35
|
-
...documentTools(ctx),
|
|
36
|
-
...notificationTools(ctx),
|
|
37
|
-
...profileTools(ctx),
|
|
38
|
-
...usageTools(ctx),
|
|
39
|
-
...settingsTools(ctx),
|
|
40
|
-
...chatHistoryTools(ctx),
|
|
41
|
-
],
|
|
42
|
-
});
|
|
128
|
+
return createToolServer(projectId, onToolResult).asMcpServer();
|
|
43
129
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
Globe,
|
|
13
13
|
Sun,
|
|
14
14
|
CheckCheck,
|
|
15
|
+
Sparkles,
|
|
15
16
|
} from "lucide-react";
|
|
16
17
|
import type { LucideIcon } from "lucide-react";
|
|
17
18
|
|
|
@@ -25,6 +26,7 @@ export type ToolGroup =
|
|
|
25
26
|
| "Documents"
|
|
26
27
|
| "Notifications"
|
|
27
28
|
| "Profiles"
|
|
29
|
+
| "Skills"
|
|
28
30
|
| "Usage"
|
|
29
31
|
| "Settings"
|
|
30
32
|
| "Chat"
|
|
@@ -54,6 +56,7 @@ export const TOOL_GROUP_ICONS: Record<ToolGroup, LucideIcon> = {
|
|
|
54
56
|
Documents: FileText,
|
|
55
57
|
Notifications: Bell,
|
|
56
58
|
Profiles: Bot,
|
|
59
|
+
Skills: Sparkles,
|
|
57
60
|
Usage: Wallet,
|
|
58
61
|
Settings: Settings,
|
|
59
62
|
Chat: MessageSquare,
|
|
@@ -69,6 +72,7 @@ export const TOOL_GROUP_ORDER: ToolGroup[] = [
|
|
|
69
72
|
"Documents",
|
|
70
73
|
"Schedules",
|
|
71
74
|
"Profiles",
|
|
75
|
+
"Skills",
|
|
72
76
|
"Browser",
|
|
73
77
|
"Notifications",
|
|
74
78
|
"Chat",
|
|
@@ -174,6 +178,26 @@ export function getToolCatalog(opts?: { includeBrowser?: boolean }): ToolCatalog
|
|
|
174
178
|
return cachedCatalog;
|
|
175
179
|
}
|
|
176
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Get the tool catalog with dynamic project skills appended.
|
|
183
|
+
* NOT cached at module level because it depends on the active project.
|
|
184
|
+
*/
|
|
185
|
+
export function getToolCatalogWithSkills(opts?: {
|
|
186
|
+
includeBrowser?: boolean;
|
|
187
|
+
projectProfiles?: Array<{ id: string; name: string; description: string }>;
|
|
188
|
+
}): ToolCatalogEntry[] {
|
|
189
|
+
const base = getToolCatalog(opts);
|
|
190
|
+
if (!opts?.projectProfiles?.length) return base;
|
|
191
|
+
|
|
192
|
+
const skillEntries: ToolCatalogEntry[] = opts.projectProfiles.map((p) => ({
|
|
193
|
+
name: p.id,
|
|
194
|
+
description: p.description,
|
|
195
|
+
group: "Skills" as ToolGroup,
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
return [...base, ...skillEntries];
|
|
199
|
+
}
|
|
200
|
+
|
|
177
201
|
/** Group catalog entries by their ToolGroup */
|
|
178
202
|
export function groupToolCatalog(entries: ToolCatalogEntry[]): Record<string, ToolCatalogEntry[]> {
|
|
179
203
|
const groups: Record<string, ToolCatalogEntry[]> = {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-agnostic tool definition registry.
|
|
3
|
+
*
|
|
4
|
+
* `defineTool()` replaces the SDK's `tool()` so that tool definitions
|
|
5
|
+
* are decoupled from any specific provider SDK. The resulting
|
|
6
|
+
* `ToolDefinition` objects carry both the original Zod schema (for
|
|
7
|
+
* backward-compatible SDK wrapping) and a pre-computed JSON Schema
|
|
8
|
+
* (for direct API runtimes).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
// ── Types ────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
/** MCP-compatible tool result (matches ok/err helper output). */
|
|
16
|
+
export interface ToolResult {
|
|
17
|
+
content: Array<{ type: "text"; text: string }>;
|
|
18
|
+
isError?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Provider-neutral tool definition (type-erased for storage in arrays). */
|
|
22
|
+
export interface ToolDefinition {
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
/** Original Zod shape — retained for SDK bridge wrapping. */
|
|
26
|
+
zodShape: z.ZodRawShape;
|
|
27
|
+
/** Pre-computed JSON Schema (Draft 2020-12) for direct API runtimes. */
|
|
28
|
+
inputSchema: Record<string, unknown>;
|
|
29
|
+
/** Async handler that receives validated args and returns MCP content. */
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
handler: (args: any) => Promise<ToolResult>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ── Factory ──────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Define a provider-agnostic tool.
|
|
38
|
+
*
|
|
39
|
+
* Signature mirrors the SDK's `tool(name, description, zodShape, handler)`
|
|
40
|
+
* so that converting existing tool files is a simple import swap.
|
|
41
|
+
*/
|
|
42
|
+
export function defineTool<T extends z.ZodRawShape>(
|
|
43
|
+
name: string,
|
|
44
|
+
description: string,
|
|
45
|
+
inputShape: T,
|
|
46
|
+
handler: (args: z.infer<z.ZodObject<T>>) => Promise<ToolResult>,
|
|
47
|
+
): ToolDefinition {
|
|
48
|
+
const zodObject = z.object(inputShape);
|
|
49
|
+
const inputSchema = z.toJSONSchema(zodObject) as Record<string, unknown>;
|
|
50
|
+
|
|
51
|
+
return { name, description, zodShape: inputShape, inputSchema, handler };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Provider formatters ──────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
/** Format for Anthropic Messages API `tools` parameter. */
|
|
57
|
+
export interface AnthropicToolDef {
|
|
58
|
+
name: string;
|
|
59
|
+
description: string;
|
|
60
|
+
input_schema: Record<string, unknown>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function toAnthropicToolDef(def: ToolDefinition): AnthropicToolDef {
|
|
64
|
+
return {
|
|
65
|
+
name: def.name,
|
|
66
|
+
description: def.description,
|
|
67
|
+
input_schema: def.inputSchema,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Format for OpenAI Responses API function tool. */
|
|
72
|
+
export interface OpenAIFunctionDef {
|
|
73
|
+
type: "function";
|
|
74
|
+
function: {
|
|
75
|
+
name: string;
|
|
76
|
+
description: string;
|
|
77
|
+
parameters: Record<string, unknown>;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function toOpenAIFunctionDef(def: ToolDefinition): OpenAIFunctionDef {
|
|
82
|
+
return {
|
|
83
|
+
type: "function",
|
|
84
|
+
function: {
|
|
85
|
+
name: def.name,
|
|
86
|
+
description: def.description,
|
|
87
|
+
parameters: def.inputSchema,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineTool } from "../tool-registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { ok, err, type ToolContext } from "./helpers";
|
|
4
4
|
import {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
export function chatHistoryTools(ctx: ToolContext) {
|
|
12
12
|
return [
|
|
13
13
|
// ── list_conversations ──────────────────────────────────────────
|
|
14
|
-
|
|
14
|
+
defineTool(
|
|
15
15
|
"list_conversations",
|
|
16
16
|
"List recent chat conversations. Use to find past discussions, filter by project or status, or search titles.",
|
|
17
17
|
{
|
|
@@ -59,7 +59,7 @@ export function chatHistoryTools(ctx: ToolContext) {
|
|
|
59
59
|
),
|
|
60
60
|
|
|
61
61
|
// ── get_conversation_messages ────────────────────────────────────
|
|
62
|
-
|
|
62
|
+
defineTool(
|
|
63
63
|
"get_conversation_messages",
|
|
64
64
|
"Get message history from a past conversation. Use to recall what was discussed, review decisions, or find specific information from a prior chat.",
|
|
65
65
|
{
|
|
@@ -119,7 +119,7 @@ export function chatHistoryTools(ctx: ToolContext) {
|
|
|
119
119
|
),
|
|
120
120
|
|
|
121
121
|
// ── search_messages ─────────────────────────────────────────────
|
|
122
|
-
|
|
122
|
+
defineTool(
|
|
123
123
|
"search_messages",
|
|
124
124
|
"Search across all conversations for specific content. Use when the user asks about prior discussions, decisions, or any topic from past chats.",
|
|
125
125
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineTool } from "../tool-registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { db } from "@/lib/db";
|
|
4
4
|
import { documents } from "@/lib/db/schema";
|
|
@@ -44,7 +44,7 @@ function resolveMimeType(filename: string): string {
|
|
|
44
44
|
|
|
45
45
|
export function documentTools(ctx: ToolContext) {
|
|
46
46
|
return [
|
|
47
|
-
|
|
47
|
+
defineTool(
|
|
48
48
|
"list_documents",
|
|
49
49
|
"List documents, optionally filtered by project, task, direction, or status.",
|
|
50
50
|
{
|
|
@@ -97,7 +97,7 @@ export function documentTools(ctx: ToolContext) {
|
|
|
97
97
|
}
|
|
98
98
|
),
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
defineTool(
|
|
101
101
|
"get_document",
|
|
102
102
|
"Get metadata for a specific document (does not return file content).",
|
|
103
103
|
{
|
|
@@ -134,7 +134,7 @@ export function documentTools(ctx: ToolContext) {
|
|
|
134
134
|
}
|
|
135
135
|
),
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
defineTool(
|
|
138
138
|
"upload_document",
|
|
139
139
|
"Upload a file from the filesystem as a document. Use this to register files you create as documents in the Documents library. The file is copied to Stagent storage and queued for preprocessing (text extraction).",
|
|
140
140
|
{
|
|
@@ -194,7 +194,7 @@ export function documentTools(ctx: ToolContext) {
|
|
|
194
194
|
}
|
|
195
195
|
),
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
defineTool(
|
|
198
198
|
"update_document",
|
|
199
199
|
"Update document metadata or trigger reprocessing. Metadata is merged with existing values, not replaced.",
|
|
200
200
|
{
|
|
@@ -255,7 +255,7 @@ export function documentTools(ctx: ToolContext) {
|
|
|
255
255
|
}
|
|
256
256
|
),
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
defineTool(
|
|
259
259
|
"delete_document",
|
|
260
260
|
"Delete a document. If the document is linked to a task, you must set cascadeDelete to true to confirm deletion.",
|
|
261
261
|
{
|
|
@@ -300,7 +300,7 @@ export function documentTools(ctx: ToolContext) {
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
),
|
|
303
|
-
|
|
303
|
+
defineTool(
|
|
304
304
|
"read_document_content",
|
|
305
305
|
"Read the full extracted text content of a document. Use this when you need to analyze, summarize, or answer questions about a document's contents.",
|
|
306
306
|
{
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { defineTool } from "../tool-registry";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { ok, err, type ToolContext } from "./helpers";
|
|
4
|
+
import { sendHandoff } from "@/lib/agents/handoff/bus";
|
|
5
|
+
|
|
6
|
+
export function handoffTools(_ctx: ToolContext) {
|
|
7
|
+
return [
|
|
8
|
+
defineTool(
|
|
9
|
+
"send_handoff",
|
|
10
|
+
"Hand off a task from one agent profile to another. Creates an async handoff request that the target agent will pick up. Use this when a task requires expertise from a different agent profile.",
|
|
11
|
+
{
|
|
12
|
+
toProfile: z
|
|
13
|
+
.string()
|
|
14
|
+
.describe("Target agent profile ID (e.g. code-reviewer, researcher, document-writer)"),
|
|
15
|
+
subject: z
|
|
16
|
+
.string()
|
|
17
|
+
.min(1)
|
|
18
|
+
.max(200)
|
|
19
|
+
.describe("Brief subject line for the handoff"),
|
|
20
|
+
body: z
|
|
21
|
+
.string()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(4000)
|
|
24
|
+
.describe("Detailed description of what the target agent should do"),
|
|
25
|
+
fromProfile: z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("Source agent profile ID. Defaults to 'general'."),
|
|
29
|
+
sourceTaskId: z
|
|
30
|
+
.string()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe("Source task ID that initiated this handoff"),
|
|
33
|
+
priority: z
|
|
34
|
+
.number()
|
|
35
|
+
.min(0)
|
|
36
|
+
.max(3)
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Priority: 0 = critical, 1 = high, 2 = medium (default), 3 = low"),
|
|
39
|
+
requiresApproval: z
|
|
40
|
+
.boolean()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Whether human approval is required before the handoff is processed. Defaults to false."),
|
|
43
|
+
},
|
|
44
|
+
async (args) => {
|
|
45
|
+
try {
|
|
46
|
+
const messageId = await sendHandoff({
|
|
47
|
+
fromProfileId: args.fromProfile ?? "general",
|
|
48
|
+
toProfileId: args.toProfile,
|
|
49
|
+
sourceTaskId: args.sourceTaskId ?? "",
|
|
50
|
+
subject: args.subject,
|
|
51
|
+
body: args.body,
|
|
52
|
+
priority: args.priority,
|
|
53
|
+
requiresApproval: args.requiresApproval,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return ok({
|
|
57
|
+
message: "Handoff created successfully",
|
|
58
|
+
messageId,
|
|
59
|
+
from: args.fromProfile ?? "general",
|
|
60
|
+
to: args.toProfile,
|
|
61
|
+
subject: args.subject,
|
|
62
|
+
requiresApproval: args.requiresApproval ?? false,
|
|
63
|
+
});
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return err(e instanceof Error ? e.message : "Failed to create handoff");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
];
|
|
70
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineTool } from "../tool-registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { db } from "@/lib/db";
|
|
4
4
|
import { notifications } from "@/lib/db/schema";
|
|
@@ -7,7 +7,7 @@ import { ok, err, type ToolContext } from "./helpers";
|
|
|
7
7
|
|
|
8
8
|
export function notificationTools(_ctx: ToolContext) {
|
|
9
9
|
return [
|
|
10
|
-
|
|
10
|
+
defineTool(
|
|
11
11
|
"list_notifications",
|
|
12
12
|
"List notifications. By default shows only pending approval requests. Set pendingOnly to false for all recent notifications.",
|
|
13
13
|
{
|
|
@@ -58,7 +58,7 @@ export function notificationTools(_ctx: ToolContext) {
|
|
|
58
58
|
}
|
|
59
59
|
),
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
defineTool(
|
|
62
62
|
"respond_notification",
|
|
63
63
|
"Respond to a pending permission or approval notification. Use 'allow' to approve or 'deny' to reject.",
|
|
64
64
|
{
|
|
@@ -124,7 +124,7 @@ export function notificationTools(_ctx: ToolContext) {
|
|
|
124
124
|
}
|
|
125
125
|
),
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
defineTool(
|
|
128
128
|
"mark_notifications_read",
|
|
129
129
|
"Mark all unread notifications as read.",
|
|
130
130
|
{},
|