@tuturuuu/ai 0.0.10
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 +76 -0
- package/package.json +106 -0
- package/src/api-key-hash.ts +28 -0
- package/src/calendar/events.ts +34 -0
- package/src/calendar/route.ts +114 -0
- package/src/chat/credit-source.ts +1 -0
- package/src/chat/google/chat-request-schema.ts +150 -0
- package/src/chat/google/default-system-instruction.ts +198 -0
- package/src/chat/google/message-file-processing.ts +212 -0
- package/src/chat/google/mira-step-preparation.ts +221 -0
- package/src/chat/google/new/route.ts +368 -0
- package/src/chat/google/route-auth.ts +81 -0
- package/src/chat/google/route-chat-resolution.ts +98 -0
- package/src/chat/google/route-credits.ts +61 -0
- package/src/chat/google/route-message-preparation.ts +331 -0
- package/src/chat/google/route-mira-runtime.ts +206 -0
- package/src/chat/google/route.ts +632 -0
- package/src/chat/google/stream-finish-persistence.ts +722 -0
- package/src/chat/google/summary/route.ts +153 -0
- package/src/chat/mira-render-ui-policy.ts +540 -0
- package/src/chat/mira-system-instruction.ts +484 -0
- package/src/chat-sdk/adapters.ts +389 -0
- package/src/chat-sdk/registry.ts +197 -0
- package/src/chat-sdk.ts +33 -0
- package/src/core.ts +3 -0
- package/src/credits/cap-output-tokens.ts +90 -0
- package/src/credits/check-credits.ts +232 -0
- package/src/credits/constants.ts +30 -0
- package/src/credits/index.ts +46 -0
- package/src/credits/model-mapping.ts +92 -0
- package/src/credits/reservations.ts +514 -0
- package/src/credits/resolve-plan-model.ts +219 -0
- package/src/credits/sync-gateway-models.ts +351 -0
- package/src/credits/types.ts +109 -0
- package/src/credits/use-ai-credits.ts +3 -0
- package/src/embeddings/metered.ts +283 -0
- package/src/executions/route.ts +137 -0
- package/src/generate/route.ts +411 -0
- package/src/hooks.ts +7 -0
- package/src/meetings/summary/route.ts +7 -0
- package/src/meetings/transcription/route.ts +134 -0
- package/src/memory/client.ts +158 -0
- package/src/memory/config.ts +38 -0
- package/src/memory/index.ts +32 -0
- package/src/memory/ingest.ts +51 -0
- package/src/memory/middleware.ts +35 -0
- package/src/memory/operations.ts +480 -0
- package/src/memory/scope.ts +102 -0
- package/src/memory/settings.ts +121 -0
- package/src/memory/types.ts +101 -0
- package/src/memory/workspace.ts +36 -0
- package/src/memory.ts +1 -0
- package/src/mind/patch.ts +146 -0
- package/src/mind/route.ts +687 -0
- package/src/mind/tools.ts +1500 -0
- package/src/mind/types.ts +20 -0
- package/src/object/core.ts +3 -0
- package/src/object/flashcards/route.ts +140 -0
- package/src/object/quizzes/explanation/route.ts +145 -0
- package/src/object/quizzes/route.ts +142 -0
- package/src/object/types.ts +187 -0
- package/src/object/year-plan/route.ts +196 -0
- package/src/react.ts +1 -0
- package/src/scheduling/algorithm.ts +791 -0
- package/src/scheduling/default.ts +36 -0
- package/src/scheduling/duration-optimizer.ts +689 -0
- package/src/scheduling/index.ts +79 -0
- package/src/scheduling/priority-calculator.ts +187 -0
- package/src/scheduling/recurrence-calculator.ts +621 -0
- package/src/scheduling/templates.ts +892 -0
- package/src/scheduling/types.ts +136 -0
- package/src/scheduling/web-adapter.ts +308 -0
- package/src/scheduling.ts +6 -0
- package/src/supported-actions.ts +1 -0
- package/src/supported-providers.ts +6 -0
- package/src/tools/context-builder.ts +372 -0
- package/src/tools/core.ts +1 -0
- package/src/tools/definitions/calendar.ts +106 -0
- package/src/tools/definitions/finance.ts +197 -0
- package/src/tools/definitions/image.ts +74 -0
- package/src/tools/definitions/memory.ts +83 -0
- package/src/tools/definitions/meta.ts +154 -0
- package/src/tools/definitions/render-ui.ts +81 -0
- package/src/tools/definitions/tasks.ts +343 -0
- package/src/tools/definitions/time-tracking.ts +381 -0
- package/src/tools/definitions/workspace-context.ts +45 -0
- package/src/tools/definitions/workspace-user-chat.ts +111 -0
- package/src/tools/executors/calendar.ts +371 -0
- package/src/tools/executors/chat.ts +15 -0
- package/src/tools/executors/finance.ts +638 -0
- package/src/tools/executors/helpers/encryption.ts +107 -0
- package/src/tools/executors/image.ts +247 -0
- package/src/tools/executors/markitdown.ts +684 -0
- package/src/tools/executors/memory.ts +277 -0
- package/src/tools/executors/parallel-checks.ts +176 -0
- package/src/tools/executors/qr.ts +170 -0
- package/src/tools/executors/scope-helpers.ts +192 -0
- package/src/tools/executors/search.ts +149 -0
- package/src/tools/executors/settings.ts +40 -0
- package/src/tools/executors/tasks.ts +1087 -0
- package/src/tools/executors/theme.ts +23 -0
- package/src/tools/executors/timer/timer-categories-executor.ts +110 -0
- package/src/tools/executors/timer/timer-category-mutations.ts +240 -0
- package/src/tools/executors/timer/timer-goal-mutations.ts +323 -0
- package/src/tools/executors/timer/timer-goals-executor.ts +272 -0
- package/src/tools/executors/timer/timer-helpers.ts +372 -0
- package/src/tools/executors/timer/timer-mutation-schemas.ts +160 -0
- package/src/tools/executors/timer/timer-mutation-types.ts +212 -0
- package/src/tools/executors/timer/timer-mutations.ts +19 -0
- package/src/tools/executors/timer/timer-queries.ts +18 -0
- package/src/tools/executors/timer/timer-session-lifecycle.ts +299 -0
- package/src/tools/executors/timer/timer-session-mutations.ts +10 -0
- package/src/tools/executors/timer/timer-session-queries.ts +153 -0
- package/src/tools/executors/timer/timer-session-updates.ts +200 -0
- package/src/tools/executors/timer/timer-sessions-executor.ts +91 -0
- package/src/tools/executors/timer/timer-stats-executor.ts +157 -0
- package/src/tools/executors/timer.ts +22 -0
- package/src/tools/executors/user.ts +60 -0
- package/src/tools/executors/workspace.ts +135 -0
- package/src/tools/json-render-catalog.ts +875 -0
- package/src/tools/mira-tool-definitions.ts +55 -0
- package/src/tools/mira-tool-dispatcher.ts +265 -0
- package/src/tools/mira-tool-metadata.ts +164 -0
- package/src/tools/mira-tool-names.ts +95 -0
- package/src/tools/mira-tool-render-ui.ts +54 -0
- package/src/tools/mira-tool-types.ts +17 -0
- package/src/tools/mira-tools.ts +167 -0
- package/src/tools/normalize-render-ui-input.ts +321 -0
- package/src/tools/workspace-context.ts +233 -0
- package/src/types.ts +38 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { TaskPriority } from '@tuturuuu/types/primitives/Priority';
|
|
2
|
+
import type dayjs from 'dayjs';
|
|
3
|
+
|
|
4
|
+
export interface DateRange {
|
|
5
|
+
start: dayjs.Dayjs;
|
|
6
|
+
end: dayjs.Dayjs;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Event {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
range: DateRange;
|
|
13
|
+
taskId: string;
|
|
14
|
+
partNumber?: number;
|
|
15
|
+
totalParts?: number;
|
|
16
|
+
locked?: boolean;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}
|
|
19
|
+
export type TimeOfDayPreference = 'morning' | 'afternoon' | 'evening' | 'night';
|
|
20
|
+
|
|
21
|
+
export interface Task {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
duration: number;
|
|
25
|
+
minDuration: number;
|
|
26
|
+
maxDuration: number;
|
|
27
|
+
category: 'work' | 'personal' | 'meeting';
|
|
28
|
+
priority: TaskPriority;
|
|
29
|
+
deadline?: dayjs.Dayjs;
|
|
30
|
+
allowSplit?: boolean;
|
|
31
|
+
streak?: number;
|
|
32
|
+
energyLoad?: 'high' | 'medium' | 'low';
|
|
33
|
+
isHabit?: boolean;
|
|
34
|
+
timePreference?: TimeOfDayPreference;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type EnergyProfile =
|
|
38
|
+
| 'morning_person'
|
|
39
|
+
| 'night_owl'
|
|
40
|
+
| 'afternoon_peak'
|
|
41
|
+
| 'evening_peak';
|
|
42
|
+
|
|
43
|
+
export interface SchedulingSettings {
|
|
44
|
+
min_buffer?: number;
|
|
45
|
+
preferred_buffer?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ActiveHours {
|
|
49
|
+
personal: DateRange[];
|
|
50
|
+
work: DateRange[];
|
|
51
|
+
meeting: DateRange[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface Log {
|
|
55
|
+
type: 'warning' | 'error';
|
|
56
|
+
message: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ScheduleResult {
|
|
60
|
+
events: Event[];
|
|
61
|
+
logs: Log[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface TemplateScenario {
|
|
65
|
+
name: string;
|
|
66
|
+
description: string;
|
|
67
|
+
tasks: Task[];
|
|
68
|
+
activeHours?: Partial<ActiveHours>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Web Integration Types
|
|
73
|
+
// These types bridge the web app task format to the scheduling algorithm
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
export type CalendarHoursType =
|
|
77
|
+
| 'work_hours'
|
|
78
|
+
| 'personal_hours'
|
|
79
|
+
| 'meeting_hours';
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Input format for scheduling tasks from the web app
|
|
83
|
+
*/
|
|
84
|
+
export interface WebTaskInput {
|
|
85
|
+
id: string;
|
|
86
|
+
name?: string | null;
|
|
87
|
+
description?: string | null;
|
|
88
|
+
total_duration?: number | null; // hours
|
|
89
|
+
is_splittable?: boolean | null;
|
|
90
|
+
min_split_duration_minutes?: number | null;
|
|
91
|
+
max_split_duration_minutes?: number | null;
|
|
92
|
+
calendar_hours?: CalendarHoursType | null;
|
|
93
|
+
priority?: TaskPriority | null;
|
|
94
|
+
start_date?: string | null;
|
|
95
|
+
end_date?: string | null; // deadline
|
|
96
|
+
streak?: number | null;
|
|
97
|
+
energy_load?: 'high' | 'medium' | 'low' | null;
|
|
98
|
+
is_habit?: boolean | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Existing calendar event from the web app
|
|
103
|
+
*/
|
|
104
|
+
export interface WebCalendarEvent {
|
|
105
|
+
id?: string;
|
|
106
|
+
start_at: string;
|
|
107
|
+
end_at: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Result of scheduling with web-friendly format
|
|
112
|
+
*/
|
|
113
|
+
export interface WebScheduleResult {
|
|
114
|
+
success: boolean;
|
|
115
|
+
events: Array<{
|
|
116
|
+
id: string;
|
|
117
|
+
title: string;
|
|
118
|
+
start_at: string;
|
|
119
|
+
end_at: string;
|
|
120
|
+
task_id: string;
|
|
121
|
+
partNumber?: number;
|
|
122
|
+
totalParts?: number;
|
|
123
|
+
reason?: string;
|
|
124
|
+
}>;
|
|
125
|
+
totalScheduledMinutes: number;
|
|
126
|
+
message: string;
|
|
127
|
+
warning?: string;
|
|
128
|
+
logs: Log[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface SchedulingWeights {
|
|
132
|
+
habitIdealTimeBonus?: number;
|
|
133
|
+
habitPreferenceBonus?: number;
|
|
134
|
+
taskPreferenceBonus?: number;
|
|
135
|
+
taskBaseEarlyBonus?: number;
|
|
136
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Adapter for Task Scheduling
|
|
3
|
+
*
|
|
4
|
+
* This module bridges the web app's task format to the scheduling algorithm.
|
|
5
|
+
* It converts between web task format and the internal scheduler format.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { TaskPriority } from '@tuturuuu/types/primitives/Priority';
|
|
9
|
+
import dayjs from 'dayjs';
|
|
10
|
+
import { scheduleTasks } from './algorithm';
|
|
11
|
+
import type {
|
|
12
|
+
ActiveHours,
|
|
13
|
+
CalendarHoursType,
|
|
14
|
+
DateRange,
|
|
15
|
+
Event,
|
|
16
|
+
Task,
|
|
17
|
+
WebCalendarEvent,
|
|
18
|
+
WebScheduleResult,
|
|
19
|
+
WebTaskInput,
|
|
20
|
+
} from './types';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Map web calendar hours type to scheduler category
|
|
24
|
+
*/
|
|
25
|
+
export function mapCalendarHoursToCategory(
|
|
26
|
+
calendarHours: CalendarHoursType | null | undefined
|
|
27
|
+
): 'work' | 'personal' | 'meeting' {
|
|
28
|
+
switch (calendarHours) {
|
|
29
|
+
case 'personal_hours':
|
|
30
|
+
return 'personal';
|
|
31
|
+
case 'meeting_hours':
|
|
32
|
+
return 'meeting';
|
|
33
|
+
default:
|
|
34
|
+
return 'work';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convert web task format to scheduler task format
|
|
40
|
+
*/
|
|
41
|
+
export function convertWebTaskToSchedulerTask(
|
|
42
|
+
task: WebTaskInput,
|
|
43
|
+
existingScheduledMinutes: number = 0
|
|
44
|
+
): Task {
|
|
45
|
+
const totalHours = task.total_duration ?? 0;
|
|
46
|
+
const scheduledHours = existingScheduledMinutes / 60;
|
|
47
|
+
const remainingHours = Math.max(0, totalHours - scheduledHours);
|
|
48
|
+
|
|
49
|
+
// Convert minutes to hours for min/max duration
|
|
50
|
+
const minDurationHours = (task.min_split_duration_minutes ?? 30) / 60;
|
|
51
|
+
const maxDurationHours = (task.max_split_duration_minutes ?? 120) / 60;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
id: task.id,
|
|
55
|
+
name: task.name || 'Task',
|
|
56
|
+
duration: remainingHours,
|
|
57
|
+
minDuration: minDurationHours,
|
|
58
|
+
maxDuration: maxDurationHours,
|
|
59
|
+
category: mapCalendarHoursToCategory(task.calendar_hours),
|
|
60
|
+
priority: (task.priority as TaskPriority) ?? 'normal',
|
|
61
|
+
deadline: task.end_date ? dayjs(task.end_date) : undefined,
|
|
62
|
+
allowSplit: task.is_splittable ?? true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Convert multiple web tasks to scheduler tasks, sorted by deadline
|
|
68
|
+
*/
|
|
69
|
+
export function convertWebTasksToSchedulerTasks(
|
|
70
|
+
tasks: Array<{ task: WebTaskInput; existingScheduledMinutes?: number }>
|
|
71
|
+
): Task[] {
|
|
72
|
+
return tasks
|
|
73
|
+
.map(({ task, existingScheduledMinutes }) =>
|
|
74
|
+
convertWebTaskToSchedulerTask(task, existingScheduledMinutes ?? 0)
|
|
75
|
+
)
|
|
76
|
+
.sort((a, b) => {
|
|
77
|
+
// Sort by deadline (earliest first, null deadlines last)
|
|
78
|
+
if (!a.deadline && !b.deadline) return 0;
|
|
79
|
+
if (!a.deadline) return 1;
|
|
80
|
+
if (!b.deadline) return -1;
|
|
81
|
+
return a.deadline.valueOf() - b.deadline.valueOf();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Convert existing web calendar events to locked events format
|
|
87
|
+
*/
|
|
88
|
+
export function convertWebEventsToLocked(events: WebCalendarEvent[]): Event[] {
|
|
89
|
+
return events.map((e, i) => ({
|
|
90
|
+
id: e.id || `existing-${i}`,
|
|
91
|
+
name: 'Existing Event',
|
|
92
|
+
range: {
|
|
93
|
+
start: dayjs(e.start_at),
|
|
94
|
+
end: dayjs(e.end_at),
|
|
95
|
+
},
|
|
96
|
+
taskId: '',
|
|
97
|
+
locked: true,
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Convert week time ranges to ActiveHours format
|
|
103
|
+
* This converts the web app's hour settings to the scheduler's format
|
|
104
|
+
*/
|
|
105
|
+
export function convertHourSettingsToActiveHours(hourSettings: {
|
|
106
|
+
personalHours?: {
|
|
107
|
+
[day: string]: {
|
|
108
|
+
enabled: boolean;
|
|
109
|
+
timeBlocks: Array<{ startTime: string; endTime: string }>;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
workHours?: {
|
|
113
|
+
[day: string]: {
|
|
114
|
+
enabled: boolean;
|
|
115
|
+
timeBlocks: Array<{ startTime: string; endTime: string }>;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
meetingHours?: {
|
|
119
|
+
[day: string]: {
|
|
120
|
+
enabled: boolean;
|
|
121
|
+
timeBlocks: Array<{ startTime: string; endTime: string }>;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
}): ActiveHours {
|
|
125
|
+
const convertTimeBlocks = (
|
|
126
|
+
hours:
|
|
127
|
+
| {
|
|
128
|
+
[day: string]: {
|
|
129
|
+
enabled: boolean;
|
|
130
|
+
timeBlocks: Array<{ startTime: string; endTime: string }>;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
| undefined
|
|
134
|
+
): DateRange[] => {
|
|
135
|
+
if (!hours) {
|
|
136
|
+
// Default: 9am-5pm
|
|
137
|
+
return [
|
|
138
|
+
{
|
|
139
|
+
start: dayjs().hour(9).minute(0).second(0).millisecond(0),
|
|
140
|
+
end: dayjs().hour(17).minute(0).second(0).millisecond(0),
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// For simplicity, we'll use the first enabled day's time blocks
|
|
146
|
+
// In a more complex implementation, you'd handle per-day scheduling
|
|
147
|
+
const ranges: DateRange[] = [];
|
|
148
|
+
|
|
149
|
+
for (const dayConfig of Object.values(hours)) {
|
|
150
|
+
if (dayConfig.enabled && dayConfig.timeBlocks.length > 0) {
|
|
151
|
+
for (const block of dayConfig.timeBlocks) {
|
|
152
|
+
const [startHour, startMin] = block.startTime.split(':').map(Number);
|
|
153
|
+
const [endHour, endMin] = block.endTime.split(':').map(Number);
|
|
154
|
+
|
|
155
|
+
ranges.push({
|
|
156
|
+
start: dayjs()
|
|
157
|
+
.hour(startHour ?? 9)
|
|
158
|
+
.minute(startMin ?? 0)
|
|
159
|
+
.second(0)
|
|
160
|
+
.millisecond(0),
|
|
161
|
+
end: dayjs()
|
|
162
|
+
.hour(endHour ?? 17)
|
|
163
|
+
.minute(endMin ?? 0)
|
|
164
|
+
.second(0)
|
|
165
|
+
.millisecond(0),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
break; // Use first enabled day's config
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (ranges.length === 0) {
|
|
173
|
+
// Fallback to default
|
|
174
|
+
return [
|
|
175
|
+
{
|
|
176
|
+
start: dayjs().hour(9).minute(0).second(0).millisecond(0),
|
|
177
|
+
end: dayjs().hour(17).minute(0).second(0).millisecond(0),
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return ranges;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
personal: convertTimeBlocks(hourSettings.personalHours),
|
|
187
|
+
work: convertTimeBlocks(hourSettings.workHours),
|
|
188
|
+
meeting: convertTimeBlocks(hourSettings.meetingHours),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Schedule a single task using the AI package algorithm
|
|
194
|
+
*/
|
|
195
|
+
export function scheduleWebTask(
|
|
196
|
+
task: WebTaskInput,
|
|
197
|
+
existingEvents: WebCalendarEvent[],
|
|
198
|
+
activeHours: ActiveHours,
|
|
199
|
+
existingScheduledMinutes: number = 0
|
|
200
|
+
): WebScheduleResult {
|
|
201
|
+
const schedulerTask = convertWebTaskToSchedulerTask(
|
|
202
|
+
task,
|
|
203
|
+
existingScheduledMinutes
|
|
204
|
+
);
|
|
205
|
+
const lockedEvents = convertWebEventsToLocked(existingEvents);
|
|
206
|
+
|
|
207
|
+
const result = scheduleTasks([schedulerTask], activeHours, lockedEvents);
|
|
208
|
+
|
|
209
|
+
// Convert result to web format
|
|
210
|
+
const scheduledEvents = result.events.filter((e) => !e.locked);
|
|
211
|
+
const totalScheduledMinutes = scheduledEvents.reduce((sum, e) => {
|
|
212
|
+
const durationMinutes = e.range.end.diff(e.range.start, 'minute');
|
|
213
|
+
return sum + durationMinutes;
|
|
214
|
+
}, 0);
|
|
215
|
+
|
|
216
|
+
const pastDeadlineWarnings = result.logs.filter(
|
|
217
|
+
(log) => log.type === 'warning' && log.message.includes('past its deadline')
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
success: scheduledEvents.length > 0,
|
|
222
|
+
events: scheduledEvents.map((e) => ({
|
|
223
|
+
id: e.id,
|
|
224
|
+
title: e.name,
|
|
225
|
+
start_at: e.range.start.toISOString(),
|
|
226
|
+
end_at: e.range.end.toISOString(),
|
|
227
|
+
task_id: task.id,
|
|
228
|
+
partNumber: e.partNumber,
|
|
229
|
+
totalParts: e.totalParts,
|
|
230
|
+
reason: e.reason,
|
|
231
|
+
})),
|
|
232
|
+
totalScheduledMinutes: existingScheduledMinutes + totalScheduledMinutes,
|
|
233
|
+
message:
|
|
234
|
+
scheduledEvents.length > 0
|
|
235
|
+
? `Scheduled ${totalScheduledMinutes} minutes across ${scheduledEvents.length} event(s)`
|
|
236
|
+
: 'No available time slots found',
|
|
237
|
+
warning:
|
|
238
|
+
pastDeadlineWarnings.length > 0
|
|
239
|
+
? 'Some events scheduled after the deadline'
|
|
240
|
+
: undefined,
|
|
241
|
+
logs: result.logs,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Schedule multiple tasks using the AI package algorithm
|
|
247
|
+
* Tasks are scheduled in deadline order (earliest first)
|
|
248
|
+
*/
|
|
249
|
+
export function scheduleWebTasks(
|
|
250
|
+
tasks: Array<{ task: WebTaskInput; existingScheduledMinutes?: number }>,
|
|
251
|
+
existingEvents: WebCalendarEvent[],
|
|
252
|
+
activeHours: ActiveHours
|
|
253
|
+
): WebScheduleResult[] {
|
|
254
|
+
const schedulerTasks = convertWebTasksToSchedulerTasks(tasks);
|
|
255
|
+
const lockedEvents = convertWebEventsToLocked(existingEvents);
|
|
256
|
+
|
|
257
|
+
const result = scheduleTasks(schedulerTasks, activeHours, lockedEvents);
|
|
258
|
+
|
|
259
|
+
// Group events by task ID
|
|
260
|
+
const eventsByTask = new Map<string, Event[]>();
|
|
261
|
+
for (const event of result.events) {
|
|
262
|
+
if (event.locked) continue;
|
|
263
|
+
const taskEvents = eventsByTask.get(event.taskId) || [];
|
|
264
|
+
taskEvents.push(event);
|
|
265
|
+
eventsByTask.set(event.taskId, taskEvents);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Convert to web results for each task
|
|
269
|
+
return tasks.map(({ task, existingScheduledMinutes = 0 }) => {
|
|
270
|
+
const taskEvents = eventsByTask.get(task.id) || [];
|
|
271
|
+
const totalScheduledMinutes = taskEvents.reduce((sum, e) => {
|
|
272
|
+
const durationMinutes = e.range.end.diff(e.range.start, 'minute');
|
|
273
|
+
return sum + durationMinutes;
|
|
274
|
+
}, 0);
|
|
275
|
+
|
|
276
|
+
const taskLogs = result.logs.filter((log) =>
|
|
277
|
+
log.message.includes(task.name || task.id)
|
|
278
|
+
);
|
|
279
|
+
const pastDeadlineWarnings = taskLogs.filter(
|
|
280
|
+
(log) =>
|
|
281
|
+
log.type === 'warning' && log.message.includes('past its deadline')
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
success: taskEvents.length > 0,
|
|
286
|
+
events: taskEvents.map((e) => ({
|
|
287
|
+
id: e.id,
|
|
288
|
+
title: e.name,
|
|
289
|
+
start_at: e.range.start.toISOString(),
|
|
290
|
+
end_at: e.range.end.toISOString(),
|
|
291
|
+
task_id: task.id,
|
|
292
|
+
partNumber: e.partNumber,
|
|
293
|
+
totalParts: e.totalParts,
|
|
294
|
+
reason: e.reason,
|
|
295
|
+
})),
|
|
296
|
+
totalScheduledMinutes: existingScheduledMinutes + totalScheduledMinutes,
|
|
297
|
+
message:
|
|
298
|
+
taskEvents.length > 0
|
|
299
|
+
? `Scheduled ${totalScheduledMinutes} minutes across ${taskEvents.length} event(s)`
|
|
300
|
+
: 'No available time slots found',
|
|
301
|
+
warning:
|
|
302
|
+
pastDeadlineWarnings.length > 0
|
|
303
|
+
? 'Some events scheduled after the deadline'
|
|
304
|
+
: undefined,
|
|
305
|
+
logs: taskLogs,
|
|
306
|
+
};
|
|
307
|
+
});
|
|
308
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const supportedActions = ['new', 'summary'] as const;
|