@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.
Files changed (130) hide show
  1. package/README.md +76 -0
  2. package/package.json +106 -0
  3. package/src/api-key-hash.ts +28 -0
  4. package/src/calendar/events.ts +34 -0
  5. package/src/calendar/route.ts +114 -0
  6. package/src/chat/credit-source.ts +1 -0
  7. package/src/chat/google/chat-request-schema.ts +150 -0
  8. package/src/chat/google/default-system-instruction.ts +198 -0
  9. package/src/chat/google/message-file-processing.ts +212 -0
  10. package/src/chat/google/mira-step-preparation.ts +221 -0
  11. package/src/chat/google/new/route.ts +368 -0
  12. package/src/chat/google/route-auth.ts +81 -0
  13. package/src/chat/google/route-chat-resolution.ts +98 -0
  14. package/src/chat/google/route-credits.ts +61 -0
  15. package/src/chat/google/route-message-preparation.ts +331 -0
  16. package/src/chat/google/route-mira-runtime.ts +206 -0
  17. package/src/chat/google/route.ts +632 -0
  18. package/src/chat/google/stream-finish-persistence.ts +722 -0
  19. package/src/chat/google/summary/route.ts +153 -0
  20. package/src/chat/mira-render-ui-policy.ts +540 -0
  21. package/src/chat/mira-system-instruction.ts +484 -0
  22. package/src/chat-sdk/adapters.ts +389 -0
  23. package/src/chat-sdk/registry.ts +197 -0
  24. package/src/chat-sdk.ts +33 -0
  25. package/src/core.ts +3 -0
  26. package/src/credits/cap-output-tokens.ts +90 -0
  27. package/src/credits/check-credits.ts +232 -0
  28. package/src/credits/constants.ts +30 -0
  29. package/src/credits/index.ts +46 -0
  30. package/src/credits/model-mapping.ts +92 -0
  31. package/src/credits/reservations.ts +514 -0
  32. package/src/credits/resolve-plan-model.ts +219 -0
  33. package/src/credits/sync-gateway-models.ts +351 -0
  34. package/src/credits/types.ts +109 -0
  35. package/src/credits/use-ai-credits.ts +3 -0
  36. package/src/embeddings/metered.ts +283 -0
  37. package/src/executions/route.ts +137 -0
  38. package/src/generate/route.ts +411 -0
  39. package/src/hooks.ts +7 -0
  40. package/src/meetings/summary/route.ts +7 -0
  41. package/src/meetings/transcription/route.ts +134 -0
  42. package/src/memory/client.ts +158 -0
  43. package/src/memory/config.ts +38 -0
  44. package/src/memory/index.ts +32 -0
  45. package/src/memory/ingest.ts +51 -0
  46. package/src/memory/middleware.ts +35 -0
  47. package/src/memory/operations.ts +480 -0
  48. package/src/memory/scope.ts +102 -0
  49. package/src/memory/settings.ts +121 -0
  50. package/src/memory/types.ts +101 -0
  51. package/src/memory/workspace.ts +36 -0
  52. package/src/memory.ts +1 -0
  53. package/src/mind/patch.ts +146 -0
  54. package/src/mind/route.ts +687 -0
  55. package/src/mind/tools.ts +1500 -0
  56. package/src/mind/types.ts +20 -0
  57. package/src/object/core.ts +3 -0
  58. package/src/object/flashcards/route.ts +140 -0
  59. package/src/object/quizzes/explanation/route.ts +145 -0
  60. package/src/object/quizzes/route.ts +142 -0
  61. package/src/object/types.ts +187 -0
  62. package/src/object/year-plan/route.ts +196 -0
  63. package/src/react.ts +1 -0
  64. package/src/scheduling/algorithm.ts +791 -0
  65. package/src/scheduling/default.ts +36 -0
  66. package/src/scheduling/duration-optimizer.ts +689 -0
  67. package/src/scheduling/index.ts +79 -0
  68. package/src/scheduling/priority-calculator.ts +187 -0
  69. package/src/scheduling/recurrence-calculator.ts +621 -0
  70. package/src/scheduling/templates.ts +892 -0
  71. package/src/scheduling/types.ts +136 -0
  72. package/src/scheduling/web-adapter.ts +308 -0
  73. package/src/scheduling.ts +6 -0
  74. package/src/supported-actions.ts +1 -0
  75. package/src/supported-providers.ts +6 -0
  76. package/src/tools/context-builder.ts +372 -0
  77. package/src/tools/core.ts +1 -0
  78. package/src/tools/definitions/calendar.ts +106 -0
  79. package/src/tools/definitions/finance.ts +197 -0
  80. package/src/tools/definitions/image.ts +74 -0
  81. package/src/tools/definitions/memory.ts +83 -0
  82. package/src/tools/definitions/meta.ts +154 -0
  83. package/src/tools/definitions/render-ui.ts +81 -0
  84. package/src/tools/definitions/tasks.ts +343 -0
  85. package/src/tools/definitions/time-tracking.ts +381 -0
  86. package/src/tools/definitions/workspace-context.ts +45 -0
  87. package/src/tools/definitions/workspace-user-chat.ts +111 -0
  88. package/src/tools/executors/calendar.ts +371 -0
  89. package/src/tools/executors/chat.ts +15 -0
  90. package/src/tools/executors/finance.ts +638 -0
  91. package/src/tools/executors/helpers/encryption.ts +107 -0
  92. package/src/tools/executors/image.ts +247 -0
  93. package/src/tools/executors/markitdown.ts +684 -0
  94. package/src/tools/executors/memory.ts +277 -0
  95. package/src/tools/executors/parallel-checks.ts +176 -0
  96. package/src/tools/executors/qr.ts +170 -0
  97. package/src/tools/executors/scope-helpers.ts +192 -0
  98. package/src/tools/executors/search.ts +149 -0
  99. package/src/tools/executors/settings.ts +40 -0
  100. package/src/tools/executors/tasks.ts +1087 -0
  101. package/src/tools/executors/theme.ts +23 -0
  102. package/src/tools/executors/timer/timer-categories-executor.ts +110 -0
  103. package/src/tools/executors/timer/timer-category-mutations.ts +240 -0
  104. package/src/tools/executors/timer/timer-goal-mutations.ts +323 -0
  105. package/src/tools/executors/timer/timer-goals-executor.ts +272 -0
  106. package/src/tools/executors/timer/timer-helpers.ts +372 -0
  107. package/src/tools/executors/timer/timer-mutation-schemas.ts +160 -0
  108. package/src/tools/executors/timer/timer-mutation-types.ts +212 -0
  109. package/src/tools/executors/timer/timer-mutations.ts +19 -0
  110. package/src/tools/executors/timer/timer-queries.ts +18 -0
  111. package/src/tools/executors/timer/timer-session-lifecycle.ts +299 -0
  112. package/src/tools/executors/timer/timer-session-mutations.ts +10 -0
  113. package/src/tools/executors/timer/timer-session-queries.ts +153 -0
  114. package/src/tools/executors/timer/timer-session-updates.ts +200 -0
  115. package/src/tools/executors/timer/timer-sessions-executor.ts +91 -0
  116. package/src/tools/executors/timer/timer-stats-executor.ts +157 -0
  117. package/src/tools/executors/timer.ts +22 -0
  118. package/src/tools/executors/user.ts +60 -0
  119. package/src/tools/executors/workspace.ts +135 -0
  120. package/src/tools/json-render-catalog.ts +875 -0
  121. package/src/tools/mira-tool-definitions.ts +55 -0
  122. package/src/tools/mira-tool-dispatcher.ts +265 -0
  123. package/src/tools/mira-tool-metadata.ts +164 -0
  124. package/src/tools/mira-tool-names.ts +95 -0
  125. package/src/tools/mira-tool-render-ui.ts +54 -0
  126. package/src/tools/mira-tool-types.ts +17 -0
  127. package/src/tools/mira-tools.ts +167 -0
  128. package/src/tools/normalize-render-ui-input.ts +321 -0
  129. package/src/tools/workspace-context.ts +233 -0
  130. package/src/types.ts +38 -0
@@ -0,0 +1,371 @@
1
+ import type { TablesUpdate } from '@tuturuuu/types';
2
+ import dayjs from 'dayjs';
3
+ import timezone from 'dayjs/plugin/timezone';
4
+ import utc from 'dayjs/plugin/utc';
5
+ import type { MiraToolContext } from '../mira-tools';
6
+ import { getWorkspaceContextWorkspaceId } from '../workspace-context';
7
+ import {
8
+ decryptEventsForTools,
9
+ encryptEventFieldsForTools,
10
+ getWorkspaceKeyForTools,
11
+ } from './helpers/encryption';
12
+
13
+ dayjs.extend(utc);
14
+ dayjs.extend(timezone);
15
+
16
+ export async function executeGetUpcomingEvents(
17
+ args: Record<string, unknown>,
18
+ ctx: MiraToolContext
19
+ ) {
20
+ const days = (args.num_days as number) || (args.days as number) || 7;
21
+ const tz = ctx.timezone;
22
+ const workspaceId = getWorkspaceContextWorkspaceId(ctx);
23
+
24
+ // Build the query window in the user's timezone so "today" means their
25
+ // full calendar day, not "from this UTC instant forward".
26
+ let rangeStart: dayjs.Dayjs;
27
+ let rangeEnd: dayjs.Dayjs;
28
+
29
+ if (tz) {
30
+ try {
31
+ rangeStart = dayjs().tz(tz).startOf('day');
32
+ rangeEnd = rangeStart.add(days, 'day').endOf('day');
33
+ } catch (_) {
34
+ console.warn(`Invalid timezone ${tz}, falling back to UTC`);
35
+ rangeStart = dayjs.utc().startOf('day');
36
+ rangeEnd = rangeStart.add(days, 'day').endOf('day');
37
+ }
38
+ } else {
39
+ rangeStart = dayjs.utc().startOf('day');
40
+ rangeEnd = rangeStart.add(days, 'day').endOf('day');
41
+ }
42
+
43
+ const { data: events, error } = await ctx.supabase
44
+ .from('workspace_calendar_events')
45
+ .select('id, title, description, start_at, end_at, location, is_encrypted')
46
+ .eq('ws_id', workspaceId)
47
+ .gte('start_at', rangeStart.toISOString())
48
+ .lte('start_at', rangeEnd.toISOString())
49
+ .order('start_at', { ascending: true })
50
+ .limit(50);
51
+
52
+ if (error) return { error: error.message };
53
+ if (!events?.length) return { count: 0, events: [] };
54
+
55
+ const decrypted = await decryptEventsForTools(events, workspaceId);
56
+
57
+ // Format dates in the user's timezone so the model presents local times
58
+ const formatInTz = (iso: string) => {
59
+ if (!tz) return iso;
60
+ try {
61
+ return dayjs(iso).tz(tz).format('YYYY-MM-DDTHH:mm:ssZ');
62
+ } catch {
63
+ return iso;
64
+ }
65
+ };
66
+
67
+ return {
68
+ count: decrypted.length,
69
+ timezone: tz ?? 'UTC',
70
+ events: decrypted.map(
71
+ (e: {
72
+ id: string;
73
+ title: string;
74
+ description?: string;
75
+ start_at: string;
76
+ end_at: string;
77
+ location: string | null;
78
+ }) => ({
79
+ id: e.id,
80
+ title: e.title,
81
+ description: e.description || null,
82
+ start: formatInTz(e.start_at),
83
+ end: formatInTz(e.end_at),
84
+ location: e.location,
85
+ })
86
+ ),
87
+ };
88
+ }
89
+
90
+ export async function executeCreateEvent(
91
+ args: Record<string, unknown>,
92
+ ctx: MiraToolContext
93
+ ) {
94
+ const workspaceId = getWorkspaceContextWorkspaceId(ctx);
95
+ const title = args.title as string;
96
+ const description = (args.description as string) ?? '';
97
+ const location = (args.location as string) ?? null;
98
+
99
+ // Convert standard ISO strings or casual strings into proper UTC timestamps
100
+ // based on the provided timezone, assuming the LLM provides dates in the local timezone context.
101
+ let startAt = args.startAt as string;
102
+ let endAt = args.endAt as string;
103
+
104
+ if (ctx.timezone) {
105
+ try {
106
+ // If the model gave an ISO string without Z, treat it as local to the timezone
107
+ const startDayjs = startAt.includes('Z')
108
+ ? dayjs(startAt)
109
+ : dayjs.tz(startAt, ctx.timezone);
110
+ const endDayjs = endAt.includes('Z')
111
+ ? dayjs(endAt)
112
+ : dayjs.tz(endAt, ctx.timezone);
113
+
114
+ startAt = startDayjs.toISOString();
115
+ endAt = endDayjs.toISOString();
116
+ } catch (err) {
117
+ console.warn(
118
+ 'Failed to parse timezone dates, falling back to basic parsing',
119
+ err
120
+ );
121
+ // Fallback to basic Date parsing if timezone plugin fails
122
+ startAt = new Date(args.startAt as string).toISOString();
123
+ endAt = new Date(args.endAt as string).toISOString();
124
+ }
125
+ } else {
126
+ startAt = new Date(args.startAt as string).toISOString();
127
+ endAt = new Date(args.endAt as string).toISOString();
128
+ }
129
+
130
+ const encrypted = await encryptEventFieldsForTools(
131
+ { title, description, location },
132
+ workspaceId
133
+ );
134
+
135
+ const { data: event, error } = await ctx.supabase
136
+ .from('workspace_calendar_events')
137
+ .insert({
138
+ title: encrypted.title,
139
+ start_at: startAt,
140
+ end_at: endAt,
141
+ description: encrypted.description,
142
+ location: encrypted.location,
143
+ ws_id: workspaceId,
144
+ is_encrypted: encrypted.is_encrypted,
145
+ })
146
+ .select('id, title, start_at, end_at')
147
+ .single();
148
+
149
+ if (error) return { error: error.message };
150
+
151
+ return {
152
+ success: true,
153
+ message: `Event "${title}" created`,
154
+ event: encrypted.is_encrypted
155
+ ? { ...event, title, description, location }
156
+ : event,
157
+ };
158
+ }
159
+
160
+ export async function executeUpdateEvent(
161
+ args: Record<string, unknown>,
162
+ ctx: MiraToolContext
163
+ ) {
164
+ const workspaceId = getWorkspaceContextWorkspaceId(ctx);
165
+ const eventId = args.eventId as string;
166
+ const title = args.title as string | undefined;
167
+ const description = args.description as string | undefined;
168
+ const location = args.location as string | undefined;
169
+ let startAt = args.startAt as string | undefined;
170
+ let endAt = args.endAt as string | undefined;
171
+
172
+ // Convert dates if provided
173
+ if (startAt || endAt) {
174
+ if (ctx.timezone) {
175
+ try {
176
+ if (startAt) {
177
+ const startDayjs = startAt.includes('Z')
178
+ ? dayjs(startAt)
179
+ : dayjs.tz(startAt, ctx.timezone);
180
+ startAt = startDayjs.toISOString();
181
+ }
182
+ if (endAt) {
183
+ const endDayjs = endAt.includes('Z')
184
+ ? dayjs(endAt)
185
+ : dayjs.tz(endAt, ctx.timezone);
186
+ endAt = endDayjs.toISOString();
187
+ }
188
+ } catch (err) {
189
+ console.warn(
190
+ 'Failed to parse timezone dates for update, falling back',
191
+ err
192
+ );
193
+ if (startAt) startAt = new Date(startAt).toISOString();
194
+ if (endAt) endAt = new Date(endAt).toISOString();
195
+ }
196
+ } else {
197
+ if (startAt) startAt = new Date(startAt).toISOString();
198
+ if (endAt) endAt = new Date(endAt).toISOString();
199
+ }
200
+ }
201
+
202
+ // Get existing event to check encryption status
203
+ const { data: existingEvent, error: fetchError } = await ctx.supabase
204
+ .from('workspace_calendar_events')
205
+ .select('is_encrypted')
206
+ .eq('id', eventId)
207
+ .eq('ws_id', workspaceId)
208
+ .single();
209
+
210
+ if (fetchError || !existingEvent) {
211
+ return { error: fetchError?.message || 'Event not found' };
212
+ }
213
+
214
+ const updates: TablesUpdate<'workspace_calendar_events'> = {};
215
+ if (startAt) updates.start_at = startAt;
216
+ if (endAt) updates.end_at = endAt;
217
+
218
+ if (
219
+ title !== undefined ||
220
+ description !== undefined ||
221
+ location !== undefined
222
+ ) {
223
+ const fieldsToEncrypt = {
224
+ title: title ?? '', // Ensure fallback if undefined but we need to encrypt
225
+ description: description ?? '',
226
+ location: location ?? null,
227
+ };
228
+
229
+ // We only encrypt the fields that were actually provided to be updated,
230
+ // but the helper expects the full object. We will merge carefully.
231
+ const encrypted = await encryptEventFieldsForTools(
232
+ fieldsToEncrypt,
233
+ workspaceId
234
+ );
235
+
236
+ if (title !== undefined) updates.title = encrypted.title;
237
+ if (description !== undefined) updates.description = encrypted.description;
238
+ if (location !== undefined) updates.location = encrypted.location;
239
+ updates.is_encrypted = encrypted.is_encrypted;
240
+ }
241
+
242
+ const { error } = await ctx.supabase
243
+ .from('workspace_calendar_events')
244
+ .update(updates)
245
+ .eq('id', eventId)
246
+ .eq('ws_id', workspaceId);
247
+
248
+ if (error) return { error: error.message };
249
+
250
+ return {
251
+ success: true,
252
+ message: `Event updated successfully`,
253
+ };
254
+ }
255
+
256
+ export async function executeDeleteEvent(
257
+ args: Record<string, unknown>,
258
+ ctx: MiraToolContext
259
+ ) {
260
+ const workspaceId = getWorkspaceContextWorkspaceId(ctx);
261
+ const eventId = args.eventId as string;
262
+
263
+ const { error } = await ctx.supabase
264
+ .from('workspace_calendar_events')
265
+ .delete()
266
+ .eq('id', eventId)
267
+ .eq('ws_id', workspaceId);
268
+
269
+ if (error) return { error: error.message };
270
+
271
+ return {
272
+ success: true,
273
+ message: `Event deleted successfully`,
274
+ };
275
+ }
276
+
277
+ // ── E2EE Management ──
278
+
279
+ export async function executeCheckE2EEStatus(
280
+ _args: Record<string, unknown>,
281
+ ctx: MiraToolContext
282
+ ) {
283
+ const workspaceId = getWorkspaceContextWorkspaceId(ctx);
284
+ const key = await getWorkspaceKeyForTools(workspaceId);
285
+ const hasKey = key !== null;
286
+
287
+ if (!hasKey) {
288
+ return {
289
+ enabled: false,
290
+ hasKey: false,
291
+ message:
292
+ 'End-to-end encryption is not enabled for this workspace. Calendar events are stored in plaintext.',
293
+ };
294
+ }
295
+
296
+ const { count, error } = await ctx.supabase
297
+ .from('workspace_calendar_events')
298
+ .select('id', { count: 'exact', head: true })
299
+ .eq('ws_id', workspaceId)
300
+ .eq('is_encrypted', false);
301
+
302
+ return {
303
+ enabled: true,
304
+ hasKey: true,
305
+ unencryptedCount: error ? -1 : (count ?? 0),
306
+ message:
307
+ count && count > 0
308
+ ? `E2EE is enabled but ${count} event(s) are still unencrypted.`
309
+ : 'E2EE is enabled. All calendar events are encrypted.',
310
+ };
311
+ }
312
+
313
+ export async function executeEnableE2EE(
314
+ _args: Record<string, unknown>,
315
+ ctx: MiraToolContext
316
+ ) {
317
+ const workspaceId = getWorkspaceContextWorkspaceId(ctx);
318
+ try {
319
+ const {
320
+ isEncryptionEnabled,
321
+ getMasterKey,
322
+ encryptWorkspaceKey,
323
+ generateWorkspaceKey,
324
+ } = await import('@tuturuuu/utils/encryption');
325
+
326
+ if (!isEncryptionEnabled()) {
327
+ return {
328
+ success: false,
329
+ error:
330
+ 'Encryption is not configured on this server. Please contact an administrator.',
331
+ };
332
+ }
333
+
334
+ // Check if key already exists
335
+ const existingKey = await getWorkspaceKeyForTools(workspaceId);
336
+ if (existingKey) {
337
+ return {
338
+ success: true,
339
+ message: 'E2EE is already enabled for this workspace.',
340
+ alreadyEnabled: true,
341
+ };
342
+ }
343
+
344
+ const masterKey = getMasterKey();
345
+ const newKey = generateWorkspaceKey();
346
+ const encryptedKey = await encryptWorkspaceKey(newKey, masterKey);
347
+
348
+ const { createAdminClient } = await import(
349
+ '@tuturuuu/supabase/next/server'
350
+ );
351
+ const sbAdmin = await createAdminClient();
352
+
353
+ const { error } = await sbAdmin.from('workspace_encryption_keys').insert({
354
+ ws_id: workspaceId,
355
+ encrypted_key: encryptedKey,
356
+ } as never);
357
+
358
+ if (error) return { success: false, error: error.message };
359
+
360
+ return {
361
+ success: true,
362
+ message:
363
+ 'E2EE has been enabled. New calendar events will be encrypted automatically.',
364
+ };
365
+ } catch (err) {
366
+ return {
367
+ success: false,
368
+ error: `Failed to enable E2EE: ${err instanceof Error ? err.message : 'Unknown error'}`,
369
+ };
370
+ }
371
+ }
@@ -0,0 +1,15 @@
1
+ import type { MiraToolContext } from '../mira-tools';
2
+
3
+ export async function executeSetImmersiveMode(
4
+ args: Record<string, unknown>,
5
+ _ctx: MiraToolContext
6
+ ) {
7
+ const enabled = args.enabled as boolean;
8
+
9
+ return {
10
+ success: true,
11
+ action: 'set_immersive_mode',
12
+ enabled,
13
+ message: `Immersive mode ${enabled ? 'enabled' : 'disabled'}`,
14
+ };
15
+ }