refactor(api): extract assistant notifications slice
This commit is contained in:
@@ -103,6 +103,11 @@ import {
|
||||
createUserAdminExecutors,
|
||||
userAdminToolDefinitions,
|
||||
} from "./assistant-tools/user-admin.js";
|
||||
import {
|
||||
createNotificationsTasksExecutors,
|
||||
notificationInboxToolDefinitions,
|
||||
notificationTaskToolDefinitions,
|
||||
} from "./assistant-tools/notifications-tasks.js";
|
||||
import type { ToolContext, ToolDef, ToolExecutor } from "./assistant-tools/shared.js";
|
||||
import { getCommentToolEntityDescription, getCommentToolScopeSentence } from "../lib/comment-entity-registry.js";
|
||||
|
||||
@@ -2791,69 +2796,7 @@ export const TOOL_DEFINITIONS: ToolDef[] = [
|
||||
...configReadmodelToolDefinitions,
|
||||
...userAdminToolDefinitions,
|
||||
...userSelfServiceToolDefinitions,
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_notifications",
|
||||
description: "List recent notifications for the current user.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
unreadOnly: { type: "boolean", description: "Only show unread. Default: false" },
|
||||
limit: { type: "integer", description: "Max results. Default: 20" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "mark_notification_read",
|
||||
description: "Mark one notification as read, or all unread notifications when no notificationId is provided. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
notificationId: { type: "string", description: "Notification ID. Omit to mark all unread notifications as read." },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_unread_notification_count",
|
||||
description: "Count unread notifications for the current user.",
|
||||
parameters: { type: "object", properties: {} },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_notification",
|
||||
description: "Create a notification or task-style notification for a specific user. Manager or admin role required. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: { type: "string", description: "Target user ID." },
|
||||
type: { type: "string", description: "Notification type code." },
|
||||
title: { type: "string", description: "Title." },
|
||||
body: { type: "string", description: "Optional body text." },
|
||||
entityId: { type: "string", description: "Optional linked entity ID." },
|
||||
entityType: { type: "string", description: "Optional linked entity type." },
|
||||
category: { type: "string", enum: ["NOTIFICATION", "REMINDER", "TASK", "APPROVAL"] },
|
||||
priority: { type: "string", enum: ["LOW", "NORMAL", "HIGH", "URGENT"] },
|
||||
link: { type: "string", description: "Optional deep link." },
|
||||
taskStatus: { type: "string", enum: ["OPEN", "IN_PROGRESS", "DONE", "DISMISSED"] },
|
||||
taskAction: { type: "string", description: "Optional machine-readable task action." },
|
||||
assigneeId: { type: "string", description: "Optional assignee user ID." },
|
||||
dueDate: { type: "string", format: "date-time", description: "Optional due date." },
|
||||
channel: { type: "string", enum: ["in_app", "email", "both"] },
|
||||
senderId: { type: "string", description: "Optional sender override." },
|
||||
},
|
||||
required: ["userId", "type", "title"],
|
||||
},
|
||||
},
|
||||
},
|
||||
...notificationInboxToolDefinitions,
|
||||
|
||||
// ── DASHBOARD DETAIL ──
|
||||
{
|
||||
@@ -2924,240 +2867,7 @@ export const TOOL_DEFINITIONS: ToolDef[] = [
|
||||
},
|
||||
|
||||
// ── TASK MANAGEMENT ──
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_tasks",
|
||||
description: "List tasks and approvals for the current user via the real notification router, optionally including tasks assigned to them.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
status: { type: "string", enum: ["OPEN", "IN_PROGRESS", "DONE", "DISMISSED"], description: "Optional status filter." },
|
||||
includeAssigned: { type: "boolean", description: "Include tasks where the current user is assignee as well as owner. Default: true." },
|
||||
limit: { type: "integer", description: "Max results. Default: 20." },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_task_counts",
|
||||
description: "Get open, in-progress, done, dismissed, and overdue task counts for the current user.",
|
||||
parameters: { type: "object", properties: {} },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_task_detail",
|
||||
description: "Get details of a specific task/notification including linked entity information.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
taskId: { type: "string", description: "Notification/task ID" },
|
||||
},
|
||||
required: ["taskId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_task_status",
|
||||
description: "Update the status of a task. Mark as IN_PROGRESS, DONE, or DISMISSED.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
taskId: { type: "string", description: "Task/notification ID" },
|
||||
status: { type: "string", enum: ["OPEN", "IN_PROGRESS", "DONE", "DISMISSED"], description: "New status" },
|
||||
},
|
||||
required: ["taskId", "status"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "execute_task_action",
|
||||
description: "Execute the machine-readable action associated with a task. For example: approve a vacation, confirm an assignment, etc. The action is encoded in the task's taskAction field.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
taskId: { type: "string", description: "Task/notification ID containing the action to execute" },
|
||||
},
|
||||
required: ["taskId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_reminder",
|
||||
description: "Create a personal reminder for the current user via the real notification router. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
title: { type: "string", description: "Reminder title" },
|
||||
body: { type: "string", description: "Optional details" },
|
||||
remindAt: { type: "string", format: "date-time", description: "When to remind (ISO 8601 datetime)" },
|
||||
recurrence: { type: "string", enum: ["daily", "weekly", "monthly"], description: "Optional recurrence pattern" },
|
||||
entityId: { type: "string", description: "Optional: linked entity ID (project, resource, etc.)" },
|
||||
entityType: { type: "string", description: "Optional: entity type (project, resource, vacation, etc.)" },
|
||||
link: { type: "string", description: "Optional deep link." },
|
||||
},
|
||||
required: ["title", "remindAt"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_reminders",
|
||||
description: "List personal reminders for the current user via the real notification router.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: { type: "integer", description: "Max results. Default: 20." },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_reminder",
|
||||
description: "Update a personal reminder via the real notification router. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Reminder notification ID." },
|
||||
title: { type: "string", description: "Optional reminder title." },
|
||||
body: { type: "string", description: "Optional reminder body." },
|
||||
remindAt: { type: "string", format: "date-time", description: "Optional reminder timestamp." },
|
||||
recurrence: { type: ["string", "null"], enum: ["daily", "weekly", "monthly", null], description: "Optional recurrence update. Use null to clear recurrence." },
|
||||
},
|
||||
required: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_reminder",
|
||||
description: "Delete a personal reminder via the real notification router. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Reminder notification ID." },
|
||||
},
|
||||
required: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_task_for_user",
|
||||
description: "Create a task for a specific user via the real notification router. Manager or admin role required. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
userId: { type: "string", description: "Target user ID" },
|
||||
title: { type: "string", description: "Task title" },
|
||||
body: { type: "string", description: "Task description" },
|
||||
priority: { type: "string", enum: ["LOW", "NORMAL", "HIGH", "URGENT"], description: "Priority (default NORMAL)" },
|
||||
dueDate: { type: "string", format: "date-time", description: "Optional due date (ISO 8601)" },
|
||||
taskAction: { type: "string", description: "Optional machine-readable action (format: action_name:entity_id)" },
|
||||
entityId: { type: "string", description: "Optional linked entity ID" },
|
||||
entityType: { type: "string", description: "Optional entity type" },
|
||||
link: { type: "string", description: "Optional deep link." },
|
||||
channel: { type: "string", enum: ["in_app", "email", "both"], description: "Delivery channel. Default: in_app." },
|
||||
},
|
||||
required: ["userId", "title"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "assign_task",
|
||||
description: "Assign or reassign a task to another user via the real notification router. Manager or admin role required. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Task notification ID." },
|
||||
assigneeId: { type: "string", description: "User ID to assign." },
|
||||
},
|
||||
required: ["id", "assigneeId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "send_broadcast",
|
||||
description: "Create and send a broadcast notification via the real notification router. Manager or admin role required. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
title: { type: "string", description: "Notification title" },
|
||||
body: { type: "string", description: "Notification body" },
|
||||
targetType: { type: "string", enum: ["user", "role", "project", "orgUnit", "all"], description: "Target audience type" },
|
||||
targetValue: { type: "string", description: "Target value: user ID, role name (ADMIN/MANAGER/CONTROLLER/USER/VIEWER), project ID, or org unit ID" },
|
||||
category: { type: "string", enum: ["NOTIFICATION", "REMINDER", "TASK", "APPROVAL"], description: "Broadcast category. Default: NOTIFICATION." },
|
||||
priority: { type: "string", enum: ["LOW", "NORMAL", "HIGH", "URGENT"], description: "Priority (default NORMAL)" },
|
||||
channel: { type: "string", enum: ["in_app", "email", "both"], description: "Delivery channel (default in_app)" },
|
||||
link: { type: "string", description: "Optional deep-link URL" },
|
||||
scheduledAt: { type: "string", format: "date-time", description: "Optional scheduled send timestamp." },
|
||||
taskAction: { type: "string", description: "Optional machine-readable task action for task-like broadcasts." },
|
||||
dueDate: { type: "string", format: "date-time", description: "Optional due date for task-like broadcasts." },
|
||||
},
|
||||
required: ["title", "targetType"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_broadcasts",
|
||||
description: "List notification broadcasts via the real notification router. Manager or admin role required.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: { type: "integer", description: "Max results. Default: 20." },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_broadcast_detail",
|
||||
description: "Get one notification broadcast via the real notification router. Manager or admin role required.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Broadcast ID." },
|
||||
},
|
||||
required: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_notification",
|
||||
description: "Delete one of the current user's own notifications via the real notification router. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Notification ID." },
|
||||
},
|
||||
required: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
...notificationTaskToolDefinitions,
|
||||
|
||||
// ── INSIGHTS & ANOMALIES ──
|
||||
{
|
||||
@@ -5188,96 +4898,21 @@ const executors = {
|
||||
createScopedCallerContext,
|
||||
toAssistantTotpEnableError,
|
||||
}),
|
||||
|
||||
async list_notifications(params: { unreadOnly?: boolean; limit?: number }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
return caller.list({
|
||||
...(params.unreadOnly !== undefined ? { unreadOnly: params.unreadOnly } : {}),
|
||||
...(params.limit !== undefined ? { limit: Math.min(params.limit, 100) } : {}),
|
||||
});
|
||||
},
|
||||
|
||||
async mark_notification_read(params: { notificationId?: string }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
try {
|
||||
await caller.markRead({
|
||||
...(params.notificationId !== undefined ? { id: params.notificationId } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const mapped = toAssistantNotificationReadError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
message: params.notificationId ? "Notification marked as read." : "All unread notifications marked as read.",
|
||||
};
|
||||
},
|
||||
|
||||
async get_unread_notification_count(_params: Record<string, never>, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
const count = await caller.unreadCount();
|
||||
return { count };
|
||||
},
|
||||
|
||||
async create_notification(params: {
|
||||
userId: string;
|
||||
type: string;
|
||||
title: string;
|
||||
body?: string;
|
||||
entityId?: string;
|
||||
entityType?: string;
|
||||
category?: "NOTIFICATION" | "REMINDER" | "TASK" | "APPROVAL";
|
||||
priority?: "LOW" | "NORMAL" | "HIGH" | "URGENT";
|
||||
link?: string;
|
||||
taskStatus?: "OPEN" | "IN_PROGRESS" | "DONE" | "DISMISSED";
|
||||
taskAction?: string;
|
||||
assigneeId?: string;
|
||||
dueDate?: string;
|
||||
channel?: "in_app" | "email" | "both";
|
||||
senderId?: string;
|
||||
}, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
const dueDate = parseOptionalDateTime(params.dueDate, "dueDate");
|
||||
let notification;
|
||||
try {
|
||||
notification = await caller.create({
|
||||
userId: params.userId,
|
||||
type: params.type,
|
||||
title: params.title,
|
||||
...(params.body !== undefined ? { body: params.body } : {}),
|
||||
...(params.entityId !== undefined ? { entityId: params.entityId } : {}),
|
||||
...(params.entityType !== undefined ? { entityType: params.entityType } : {}),
|
||||
...(params.category !== undefined ? { category: params.category } : {}),
|
||||
...(params.priority !== undefined ? { priority: params.priority } : {}),
|
||||
...(params.link !== undefined ? { link: params.link } : {}),
|
||||
...(params.taskStatus !== undefined ? { taskStatus: params.taskStatus } : {}),
|
||||
...(params.taskAction !== undefined ? { taskAction: params.taskAction } : {}),
|
||||
...(params.assigneeId !== undefined ? { assigneeId: params.assigneeId } : {}),
|
||||
...(dueDate ? { dueDate } : {}),
|
||||
...(params.channel !== undefined ? { channel: params.channel } : {}),
|
||||
...(params.senderId !== undefined ? { senderId: params.senderId } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const mapped = toAssistantNotificationCreationError(error, "notification");
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
notification,
|
||||
notificationId: notification?.id ?? null,
|
||||
message: `Created notification "${params.title}".`,
|
||||
};
|
||||
},
|
||||
...createNotificationsTasksExecutors({
|
||||
createNotificationCaller,
|
||||
createScopedCallerContext,
|
||||
parseDateTime,
|
||||
parseOptionalDateTime,
|
||||
toAssistantTaskNotFoundError,
|
||||
toAssistantTaskActionError,
|
||||
toAssistantTaskAssignmentError,
|
||||
toAssistantBroadcastNotFoundError,
|
||||
toAssistantReminderNotFoundError,
|
||||
toAssistantNotificationReadError,
|
||||
toAssistantNotificationDeletionError,
|
||||
toAssistantReminderCreationError,
|
||||
toAssistantNotificationCreationError,
|
||||
}),
|
||||
|
||||
// ── DASHBOARD DETAIL ──
|
||||
|
||||
@@ -5346,361 +4981,6 @@ const executors = {
|
||||
|
||||
return { __action: "invalidate", scope: ["project"], success: true, message: `Removed cover art from project "${project.name}"` };
|
||||
},
|
||||
|
||||
// ── TASK MANAGEMENT ──
|
||||
|
||||
async list_tasks(params: {
|
||||
status?: "OPEN" | "IN_PROGRESS" | "DONE" | "DISMISSED";
|
||||
includeAssigned?: boolean;
|
||||
limit?: number;
|
||||
}, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
return caller.listTasks({
|
||||
...(params.status !== undefined ? { status: params.status } : {}),
|
||||
...(params.includeAssigned !== undefined ? { includeAssigned: params.includeAssigned } : {}),
|
||||
...(params.limit !== undefined ? { limit: Math.min(params.limit, 100) } : {}),
|
||||
});
|
||||
},
|
||||
|
||||
async get_task_counts(_params: Record<string, never>, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
return caller.taskCounts();
|
||||
},
|
||||
|
||||
async get_task_detail(params: { taskId: string }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
try {
|
||||
return await caller.getTaskDetail({ id: params.taskId });
|
||||
} catch (error) {
|
||||
const mapped = toAssistantTaskNotFoundError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async update_task_status(params: { taskId: string; status: string }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
let task;
|
||||
try {
|
||||
task = await caller.updateTaskStatus({
|
||||
id: params.taskId,
|
||||
status: params.status as "OPEN" | "IN_PROGRESS" | "DONE" | "DISMISSED",
|
||||
});
|
||||
} catch (error) {
|
||||
const mapped = toAssistantTaskNotFoundError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
task,
|
||||
message: `Task status updated to ${task.taskStatus ?? params.status}.`,
|
||||
};
|
||||
},
|
||||
|
||||
async execute_task_action(params: { taskId: string }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
let result;
|
||||
try {
|
||||
result = await caller.executeTaskAction({ id: params.taskId });
|
||||
} catch (error) {
|
||||
const mapped = toAssistantTaskActionError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
task: result.task,
|
||||
message: result.actionResult.message,
|
||||
};
|
||||
},
|
||||
|
||||
async create_reminder(params: {
|
||||
title: string;
|
||||
body?: string;
|
||||
remindAt: string;
|
||||
recurrence?: string;
|
||||
entityId?: string;
|
||||
entityType?: string;
|
||||
link?: string;
|
||||
}, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
if (!params.title.trim()) {
|
||||
return { error: "Reminder title is required." };
|
||||
}
|
||||
if (params.title.length > 200) {
|
||||
return { error: "Reminder title must be at most 200 characters." };
|
||||
}
|
||||
if (params.body !== undefined && params.body.length > 2000) {
|
||||
return { error: "Reminder body must be at most 2000 characters." };
|
||||
}
|
||||
if (
|
||||
params.recurrence !== undefined
|
||||
&& !["daily", "weekly", "monthly"].includes(params.recurrence)
|
||||
) {
|
||||
return {
|
||||
error: `Invalid recurrence: ${params.recurrence}. Valid values: daily, weekly, monthly.`,
|
||||
};
|
||||
}
|
||||
const remindAt = parseDateTime(params.remindAt, "remindAt");
|
||||
let reminder;
|
||||
try {
|
||||
reminder = await caller.createReminder({
|
||||
title: params.title,
|
||||
remindAt,
|
||||
...(params.body !== undefined ? { body: params.body } : {}),
|
||||
...(params.recurrence !== undefined ? { recurrence: params.recurrence as "daily" | "weekly" | "monthly" } : {}),
|
||||
...(params.entityId !== undefined ? { entityId: params.entityId } : {}),
|
||||
...(params.entityType !== undefined ? { entityType: params.entityType } : {}),
|
||||
...(params.link !== undefined ? { link: params.link } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const mapped = toAssistantReminderCreationError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
reminder,
|
||||
reminderId: reminder.id,
|
||||
message: `Reminder "${params.title}" created.`,
|
||||
};
|
||||
},
|
||||
|
||||
async list_reminders(params: { limit?: number }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
return caller.listReminders({
|
||||
...(params.limit !== undefined ? { limit: Math.min(params.limit, 100) } : {}),
|
||||
});
|
||||
},
|
||||
|
||||
async update_reminder(params: {
|
||||
id: string;
|
||||
title?: string;
|
||||
body?: string;
|
||||
remindAt?: string;
|
||||
recurrence?: "daily" | "weekly" | "monthly" | null;
|
||||
}, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
const remindAt = parseOptionalDateTime(params.remindAt, "remindAt");
|
||||
let reminder;
|
||||
try {
|
||||
reminder = await caller.updateReminder({
|
||||
id: params.id,
|
||||
...(params.title !== undefined ? { title: params.title } : {}),
|
||||
...(params.body !== undefined ? { body: params.body } : {}),
|
||||
...(remindAt ? { remindAt } : {}),
|
||||
...(params.recurrence !== undefined ? { recurrence: params.recurrence } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const mapped = toAssistantReminderNotFoundError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
reminder,
|
||||
reminderId: reminder.id,
|
||||
message: `Updated reminder ${params.id}.`,
|
||||
};
|
||||
},
|
||||
|
||||
async delete_reminder(params: { id: string }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
try {
|
||||
await caller.deleteReminder({ id: params.id });
|
||||
} catch (error) {
|
||||
const mapped = toAssistantReminderNotFoundError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
id: params.id,
|
||||
message: `Deleted reminder ${params.id}.`,
|
||||
};
|
||||
},
|
||||
|
||||
async create_task_for_user(params: {
|
||||
userId: string;
|
||||
title: string;
|
||||
body?: string;
|
||||
priority?: string;
|
||||
dueDate?: string;
|
||||
taskAction?: string;
|
||||
entityId?: string;
|
||||
entityType?: string;
|
||||
link?: string;
|
||||
channel?: "in_app" | "email" | "both";
|
||||
}, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
const dueDate = parseOptionalDateTime(params.dueDate, "dueDate");
|
||||
let task;
|
||||
try {
|
||||
task = await caller.createTask({
|
||||
userId: params.userId,
|
||||
title: params.title,
|
||||
...(params.body !== undefined ? { body: params.body } : {}),
|
||||
...(params.priority !== undefined ? { priority: params.priority as "LOW" | "NORMAL" | "HIGH" | "URGENT" } : {}),
|
||||
...(dueDate ? { dueDate } : {}),
|
||||
...(params.taskAction !== undefined ? { taskAction: params.taskAction } : {}),
|
||||
...(params.entityId !== undefined ? { entityId: params.entityId } : {}),
|
||||
...(params.entityType !== undefined ? { entityType: params.entityType } : {}),
|
||||
...(params.link !== undefined ? { link: params.link } : {}),
|
||||
...(params.channel !== undefined ? { channel: params.channel } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const mapped = toAssistantNotificationCreationError(error, "task");
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
task,
|
||||
taskId: task?.id ?? null,
|
||||
message: `Created task "${params.title}" for ${params.userId}.`,
|
||||
};
|
||||
},
|
||||
|
||||
async assign_task(params: { id: string; assigneeId: string }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
let task;
|
||||
try {
|
||||
task = await caller.assignTask(params);
|
||||
} catch (error) {
|
||||
const mapped = toAssistantTaskAssignmentError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
task,
|
||||
taskId: task.id,
|
||||
message: `Assigned task ${params.id} to ${params.assigneeId}.`,
|
||||
};
|
||||
},
|
||||
|
||||
async send_broadcast(params: {
|
||||
title: string;
|
||||
body?: string;
|
||||
targetType: string;
|
||||
targetValue?: string;
|
||||
category?: "NOTIFICATION" | "REMINDER" | "TASK" | "APPROVAL";
|
||||
priority?: string;
|
||||
channel?: string;
|
||||
link?: string;
|
||||
scheduledAt?: string;
|
||||
taskAction?: string;
|
||||
dueDate?: string;
|
||||
}, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
const scheduledAt = parseOptionalDateTime(params.scheduledAt, "scheduledAt");
|
||||
const dueDate = parseOptionalDateTime(params.dueDate, "dueDate");
|
||||
let broadcast;
|
||||
try {
|
||||
broadcast = await caller.createBroadcast({
|
||||
title: params.title,
|
||||
targetType: params.targetType as "user" | "role" | "project" | "orgUnit" | "all",
|
||||
...(params.body !== undefined ? { body: params.body } : {}),
|
||||
...(params.link !== undefined ? { link: params.link } : {}),
|
||||
...(params.category !== undefined ? { category: params.category } : {}),
|
||||
...(params.priority !== undefined ? { priority: params.priority as "LOW" | "NORMAL" | "HIGH" | "URGENT" } : {}),
|
||||
...(params.channel !== undefined ? { channel: params.channel as "in_app" | "email" | "both" } : {}),
|
||||
...(params.targetValue !== undefined ? { targetValue: params.targetValue } : {}),
|
||||
...(scheduledAt ? { scheduledAt } : {}),
|
||||
...(params.taskAction !== undefined ? { taskAction: params.taskAction } : {}),
|
||||
...(dueDate ? { dueDate } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const mapped = toAssistantNotificationCreationError(error, "broadcast");
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
broadcast,
|
||||
broadcastId: broadcast.id,
|
||||
recipientCount: broadcast.recipientCount ?? 0,
|
||||
message: `Broadcast "${params.title}" created.`,
|
||||
};
|
||||
},
|
||||
|
||||
async list_broadcasts(params: { limit?: number }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
return caller.listBroadcasts({
|
||||
...(params.limit !== undefined ? { limit: Math.min(params.limit, 50) } : {}),
|
||||
});
|
||||
},
|
||||
|
||||
async get_broadcast_detail(params: { id: string }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
try {
|
||||
return await caller.getBroadcastById({ id: params.id });
|
||||
} catch (error) {
|
||||
const mapped = toAssistantBroadcastNotFoundError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async delete_notification(params: { id: string }, ctx: ToolContext) {
|
||||
const caller = createNotificationCaller(createScopedCallerContext(ctx));
|
||||
try {
|
||||
await caller.delete({ id: params.id });
|
||||
} catch (error) {
|
||||
const mapped = toAssistantNotificationDeletionError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
__action: "invalidate",
|
||||
scope: ["notification"],
|
||||
success: true,
|
||||
id: params.id,
|
||||
message: `Deleted notification ${params.id}.`,
|
||||
};
|
||||
},
|
||||
|
||||
// ── INSIGHTS & ANOMALIES ──────────────────────────────────────────────────
|
||||
|
||||
async detect_anomalies(_params: Record<string, never>, ctx: ToolContext) {
|
||||
|
||||
Reference in New Issue
Block a user