@agent-native/core 0.98.7 → 0.98.9

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 (81) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/docs/content/external-agents.mdx +17 -11
  4. package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +6 -2
  5. package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +6 -2
  6. package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +6 -2
  7. package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +6 -2
  8. package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +6 -2
  9. package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +6 -2
  10. package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +6 -2
  11. package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +6 -2
  12. package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +6 -2
  13. package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +6 -2
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/client/session-replay.ts +29 -7
  16. package/corpus/core/src/integrations/adapters/slack.ts +13 -2
  17. package/corpus/core/src/integrations/pending-tasks-retry-job.ts +2 -1
  18. package/corpus/core/src/integrations/pending-tasks-store.ts +1 -1
  19. package/corpus/core/src/integrations/plugin.ts +183 -48
  20. package/corpus/core/src/integrations/types.ts +7 -0
  21. package/corpus/core/src/mcp/build-server.ts +99 -26
  22. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +31 -2
  23. package/corpus/templates/calendar/AGENTS.md +11 -0
  24. package/corpus/templates/calendar/README.md +2 -1
  25. package/corpus/templates/calendar/actions/create-event.ts +7 -6
  26. package/corpus/templates/calendar/actions/event-action-helpers.ts +49 -0
  27. package/corpus/templates/calendar/actions/update-event.ts +225 -14
  28. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +185 -70
  29. package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +26 -4
  30. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +112 -69
  31. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +621 -524
  32. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +275 -164
  33. package/corpus/templates/calendar/app/components/calendar/WorkingLocationEditor.tsx +222 -0
  34. package/corpus/templates/calendar/app/hooks/use-events.ts +151 -79
  35. package/corpus/templates/calendar/app/i18n/zh-TW.ts +6 -0
  36. package/corpus/templates/calendar/app/i18n-data.ts +60 -0
  37. package/corpus/templates/calendar/app/lib/all-day-layout.ts +126 -0
  38. package/corpus/templates/calendar/app/lib/event-form-utils.ts +18 -1
  39. package/corpus/templates/calendar/app/lib/event-mutation-inputs.ts +1 -1
  40. package/corpus/templates/calendar/app/lib/working-location.ts +163 -0
  41. package/corpus/templates/calendar/app/pages/CalendarView.tsx +21 -2
  42. package/corpus/templates/calendar/changelog/2026-07-07-working-locations-from-google-calendar-now-appear-as-native-.md +6 -0
  43. package/corpus/templates/calendar/server/lib/calendar-availability.ts +2 -0
  44. package/corpus/templates/calendar/server/lib/google-api.ts +6 -1
  45. package/corpus/templates/calendar/server/lib/google-calendar.ts +49 -19
  46. package/corpus/templates/calendar/shared/api.ts +2 -0
  47. package/dist/client/session-replay.d.ts.map +1 -1
  48. package/dist/client/session-replay.js +24 -7
  49. package/dist/client/session-replay.js.map +1 -1
  50. package/dist/collab/routes.d.ts +2 -2
  51. package/dist/integrations/adapters/slack.js +11 -2
  52. package/dist/integrations/adapters/slack.js.map +1 -1
  53. package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
  54. package/dist/integrations/pending-tasks-retry-job.js +2 -1
  55. package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
  56. package/dist/integrations/pending-tasks-store.js +1 -1
  57. package/dist/integrations/pending-tasks-store.js.map +1 -1
  58. package/dist/integrations/plugin.d.ts.map +1 -1
  59. package/dist/integrations/plugin.js +140 -34
  60. package/dist/integrations/plugin.js.map +1 -1
  61. package/dist/integrations/types.d.ts +7 -0
  62. package/dist/integrations/types.d.ts.map +1 -1
  63. package/dist/integrations/types.js.map +1 -1
  64. package/dist/mcp/build-server.d.ts.map +1 -1
  65. package/dist/mcp/build-server.js +82 -28
  66. package/dist/mcp/build-server.js.map +1 -1
  67. package/dist/notifications/routes.d.ts +1 -1
  68. package/dist/observability/routes.d.ts +5 -5
  69. package/dist/secrets/routes.d.ts +9 -9
  70. package/docs/content/external-agents.mdx +17 -11
  71. package/docs/content/locales/ar-SA/external-agents.mdx +6 -2
  72. package/docs/content/locales/de-DE/external-agents.mdx +6 -2
  73. package/docs/content/locales/es-ES/external-agents.mdx +6 -2
  74. package/docs/content/locales/fr-FR/external-agents.mdx +6 -2
  75. package/docs/content/locales/hi-IN/external-agents.mdx +6 -2
  76. package/docs/content/locales/ja-JP/external-agents.mdx +6 -2
  77. package/docs/content/locales/ko-KR/external-agents.mdx +6 -2
  78. package/docs/content/locales/pt-BR/external-agents.mdx +6 -2
  79. package/docs/content/locales/zh-CN/external-agents.mdx +6 -2
  80. package/docs/content/locales/zh-TW/external-agents.mdx +6 -2
  81. package/package.json +1 -1
@@ -303,3 +303,52 @@ export function buildStatusEventFields(args: {
303
303
  : { type, customLocation: { label } },
304
304
  };
305
305
  }
306
+
307
+ function allDayDatePart(value: string): string {
308
+ const dateOnlyPattern = /^\d{4}-\d{2}-\d{2}$/;
309
+ if (dateOnlyPattern.test(value)) return value;
310
+ const date = new Date(value);
311
+ if (Number.isNaN(date.getTime())) {
312
+ throw new Error(
313
+ "All-day status events must use valid date or datetime start and end values.",
314
+ );
315
+ }
316
+ return date.toISOString().slice(0, 10);
317
+ }
318
+
319
+ function allDaySpanDays(start: string, end: string): number {
320
+ const startDate = allDayDatePart(start);
321
+ const endDate = allDayDatePart(end);
322
+ const startMs = Date.UTC(
323
+ Number(startDate.slice(0, 4)),
324
+ Number(startDate.slice(5, 7)) - 1,
325
+ Number(startDate.slice(8, 10)),
326
+ );
327
+ const endMs = Date.UTC(
328
+ Number(endDate.slice(0, 4)),
329
+ Number(endDate.slice(5, 7)) - 1,
330
+ Number(endDate.slice(8, 10)),
331
+ );
332
+ return Math.round((endMs - startMs) / 86_400_000);
333
+ }
334
+
335
+ export function validateStatusEventTiming(args: {
336
+ eventType?: "default" | "outOfOffice" | "focusTime" | "workingLocation";
337
+ allDay?: boolean;
338
+ start: string;
339
+ end: string;
340
+ }) {
341
+ if (
342
+ (args.eventType === "outOfOffice" || args.eventType === "focusTime") &&
343
+ args.allDay === true
344
+ ) {
345
+ throw new Error("Out of office and focus time events must be timed.");
346
+ }
347
+
348
+ if (args.eventType === "workingLocation" && args.allDay === true) {
349
+ const days = allDaySpanDays(args.start, args.end);
350
+ if (days !== 1) {
351
+ throw new Error("All-day working location events must be a single day.");
352
+ }
353
+ }
354
+ }
@@ -13,6 +13,7 @@ import {
13
13
  attachmentsInput,
14
14
  attendeesInput,
15
15
  buildReminderOverrides,
16
+ buildStatusEventFields,
16
17
  cliBoolean,
17
18
  googleColorIdInput,
18
19
  normalizeAttendees,
@@ -23,7 +24,9 @@ import {
23
24
  remindersInput,
24
25
  requireActionUserEmail,
25
26
  resolveOwnedAccountEmail,
27
+ validateStatusEventTiming,
26
28
  visibilityInput,
29
+ workingLocationTypeInput,
27
30
  } from "./event-action-helpers.js";
28
31
 
29
32
  function mergeAttendees(
@@ -64,6 +67,16 @@ function mergeAttendees(
64
67
  return Array.from(merged.values());
65
68
  }
66
69
 
70
+ function workingLocationTitle(
71
+ properties: NonNullable<CalendarEvent["workingLocationProperties"]>,
72
+ ): string {
73
+ if (properties.type === "homeOffice") return "Home";
74
+ if (properties.type === "officeLocation") {
75
+ return properties.officeLocation?.label || "Office";
76
+ }
77
+ return properties.customLocation?.label || "Working location";
78
+ }
79
+
67
80
  export default defineAction({
68
81
  description:
69
82
  "Update a Google Calendar event. Supports title, description, location, time, event color, attachments, reminders, and recurrence rules such as RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR.",
@@ -80,6 +93,15 @@ export default defineAction({
80
93
  title: z.string().optional().describe("New event title"),
81
94
  description: z.string().optional().describe("New event description"),
82
95
  location: z.string().optional().describe("New event location"),
96
+ workingLocationType: workingLocationTypeInput.describe(
97
+ "For existing working-location events: homeOffice, officeLocation, or customLocation. Google Calendar event types cannot be changed after creation.",
98
+ ),
99
+ workingLocationLabel: z
100
+ .string()
101
+ .optional()
102
+ .describe(
103
+ "For existing working-location events: label shown in Google Calendar.",
104
+ ),
83
105
  start: z.string().optional().describe("New start time/date as ISO string"),
84
106
  end: z.string().optional().describe("New end time/date as ISO string"),
85
107
  startTimeZone: z
@@ -92,10 +114,10 @@ export default defineAction({
92
114
  .describe("IANA timezone for the event end, e.g. America/New_York"),
93
115
  allDay: cliBoolean.optional().describe("Whether the event is all-day"),
94
116
  transparency: availabilityInput.describe(
95
- "Google Calendar availability: opaque blocks time (Busy), transparent does not block time (Free).",
117
+ "Google Calendar availability: opaque blocks time (Busy), transparent does not block time (Free). Existing working-location events are always sent to Google as transparent.",
96
118
  ),
97
119
  visibility: visibilityInput.describe(
98
- "Google Calendar visibility: default, public, private, or confidential.",
120
+ "Google Calendar visibility: default, public, private, or confidential. Existing working-location events are always sent to Google as public.",
99
121
  ),
100
122
  status: z
101
123
  .enum(["confirmed", "tentative", "cancelled"])
@@ -193,6 +215,13 @@ export default defineAction({
193
215
  reminderMethod: args.reminderMethod,
194
216
  useDefaultReminders: args.remindersUseDefault,
195
217
  });
218
+ const hasWorkingLocationPatch =
219
+ args.workingLocationType !== undefined ||
220
+ args.workingLocationLabel !== undefined;
221
+ const hasTimePatch =
222
+ args.start !== undefined ||
223
+ args.end !== undefined ||
224
+ args.allDay !== undefined;
196
225
 
197
226
  const attendeesToAdd = normalizeAttendees(args.addAttendees);
198
227
  let attendees = normalizeAttendees(args.attendees);
@@ -216,7 +245,8 @@ export default defineAction({
216
245
  attendeesToAdd !== undefined ||
217
246
  Object.keys(reminderFields).length > 0 ||
218
247
  args.addGoogleMeet === true ||
219
- args.addZoom === true;
248
+ args.addZoom === true ||
249
+ hasWorkingLocationPatch;
220
250
 
221
251
  if (!hasPatch) {
222
252
  throw new Error("No event updates provided.");
@@ -253,6 +283,91 @@ export default defineAction({
253
283
  return existingEvent;
254
284
  };
255
285
 
286
+ if (args.location !== undefined && !hasWorkingLocationPatch) {
287
+ const existingEvent = await loadExistingEvent();
288
+ if (existingEvent.eventType === "workingLocation") {
289
+ throw new Error(
290
+ "Working-location events do not support a generic location. Use workingLocationType and workingLocationLabel instead.",
291
+ );
292
+ }
293
+ }
294
+
295
+ if (hasTimePatch) {
296
+ const existingEvent = await loadExistingEvent();
297
+ const existingStatusEventType =
298
+ existingEvent.eventType === "outOfOffice" ||
299
+ existingEvent.eventType === "focusTime" ||
300
+ existingEvent.eventType === "workingLocation"
301
+ ? existingEvent.eventType
302
+ : "default";
303
+ validateStatusEventTiming({
304
+ eventType: existingStatusEventType,
305
+ allDay: args.allDay ?? existingEvent.allDay,
306
+ start: args.start ?? existingEvent.start,
307
+ end: args.end ?? existingEvent.end,
308
+ });
309
+ if (
310
+ existingEvent.eventType === "workingLocation" &&
311
+ existingEvent.workingLocationProperties
312
+ ) {
313
+ Object.assign(updates, {
314
+ eventType: "workingLocation" as const,
315
+ transparency: "transparent" as const,
316
+ visibility: "public" as const,
317
+ workingLocationProperties: existingEvent.workingLocationProperties,
318
+ });
319
+ }
320
+ }
321
+
322
+ if (hasWorkingLocationPatch) {
323
+ const existingEvent = await loadExistingEvent();
324
+ if (existingEvent.eventType !== "workingLocation") {
325
+ throw new Error(
326
+ "Working location details can only be updated on existing working-location events. Google Calendar event types cannot be changed after creation.",
327
+ );
328
+ }
329
+ const nextWorkingLocationType =
330
+ args.workingLocationType ??
331
+ existingEvent.workingLocationProperties?.type ??
332
+ "customLocation";
333
+ const existingWorkingLocationLabel =
334
+ existingEvent.workingLocationProperties?.type === "officeLocation"
335
+ ? existingEvent.workingLocationProperties.officeLocation?.label
336
+ : existingEvent.workingLocationProperties?.type === "customLocation"
337
+ ? existingEvent.workingLocationProperties.customLocation?.label
338
+ : undefined;
339
+ const nextWorkingLocationLabel =
340
+ args.workingLocationLabel ??
341
+ args.location ??
342
+ existingWorkingLocationLabel ??
343
+ existingEvent.title;
344
+ const workingLocationFields = buildStatusEventFields({
345
+ eventType: "workingLocation",
346
+ title: args.title ?? existingEvent.title,
347
+ workingLocationType: nextWorkingLocationType,
348
+ workingLocationLabel: nextWorkingLocationLabel,
349
+ });
350
+ const nextWorkingLocationProperties =
351
+ nextWorkingLocationType === "officeLocation"
352
+ ? {
353
+ type: "officeLocation" as const,
354
+ officeLocation: {
355
+ ...(existingEvent.workingLocationProperties?.type ===
356
+ "officeLocation"
357
+ ? existingEvent.workingLocationProperties.officeLocation
358
+ : {}),
359
+ label: nextWorkingLocationLabel,
360
+ },
361
+ }
362
+ : workingLocationFields.workingLocationProperties;
363
+ Object.assign(updates, {
364
+ transparency: "transparent",
365
+ visibility: "public",
366
+ workingLocationProperties: nextWorkingLocationProperties,
367
+ });
368
+ delete updates.location;
369
+ }
370
+
256
371
  if (attendeesToAdd !== undefined) {
257
372
  const existingEvent = await loadExistingEvent();
258
373
  attendees = mergeAttendees(existingEvent.attendees, attendeesToAdd);
@@ -296,16 +411,109 @@ export default defineAction({
296
411
  };
297
412
  }
298
413
 
299
- const result = await googleCalendar.updateEvent(googleEventId, updates, {
300
- account: { ownerEmail, accountEmail },
301
- sendUpdates:
302
- args.sendUpdates ??
303
- (guestNotificationMessage || (attendeesToAdd?.length ?? 0) > 0
304
- ? "all"
305
- : undefined),
306
- addGoogleMeet: args.addGoogleMeet,
307
- scope: args.scope,
308
- });
414
+ let result: Awaited<ReturnType<typeof googleCalendar.updateEvent>>;
415
+ let returnedGoogleEventId = googleEventId;
416
+ const replaceRecurringWorkingLocation =
417
+ hasWorkingLocationPatch &&
418
+ (args.scope ?? "single") === "single" &&
419
+ existingEvent?.recurringEventId &&
420
+ updates.workingLocationProperties;
421
+
422
+ if (replaceRecurringWorkingLocation) {
423
+ const hasUnsupportedReplacementPatch =
424
+ args.title !== undefined ||
425
+ args.description !== undefined ||
426
+ args.start !== undefined ||
427
+ args.end !== undefined ||
428
+ args.startTimeZone !== undefined ||
429
+ args.endTimeZone !== undefined ||
430
+ args.allDay !== undefined ||
431
+ args.status !== undefined ||
432
+ args.colorId !== undefined ||
433
+ args.attachments !== undefined ||
434
+ args.reminders !== undefined ||
435
+ args.reminderMinutes !== undefined ||
436
+ args.remindersUseDefault !== undefined ||
437
+ args.addGoogleMeet === true ||
438
+ args.addZoom === true ||
439
+ args.recurrence !== undefined ||
440
+ args.attendees !== undefined ||
441
+ args.addAttendees !== undefined ||
442
+ args.notificationMessage !== undefined;
443
+ if (hasUnsupportedReplacementPatch) {
444
+ throw new Error(
445
+ "Change the working location separately from other event fields for a single recurring occurrence.",
446
+ );
447
+ }
448
+ const now = new Date().toISOString();
449
+ const replacement = await googleCalendar.createEvent(
450
+ {
451
+ id: "",
452
+ title: workingLocationTitle(updates.workingLocationProperties!),
453
+ description: "",
454
+ start: existingEvent!.start,
455
+ end: existingEvent!.end,
456
+ startTimeZone: existingEvent!.startTimeZone,
457
+ endTimeZone: existingEvent!.endTimeZone,
458
+ location: "",
459
+ allDay: existingEvent!.allDay,
460
+ source: "google",
461
+ accountEmail,
462
+ colorId: existingEvent!.colorId,
463
+ transparency: "transparent",
464
+ visibility: "public",
465
+ status: "confirmed",
466
+ eventType: "workingLocation",
467
+ workingLocationProperties: updates.workingLocationProperties,
468
+ createdAt: now,
469
+ updatedAt: now,
470
+ },
471
+ { account: { ownerEmail, accountEmail } },
472
+ );
473
+ if (!replacement.id) {
474
+ throw new Error(
475
+ "Google did not return an id for the working location.",
476
+ );
477
+ }
478
+ try {
479
+ await googleCalendar.deleteEvent(
480
+ googleEventId,
481
+ { ownerEmail, accountEmail },
482
+ { scope: "single" },
483
+ );
484
+ } catch (error) {
485
+ try {
486
+ await googleCalendar.deleteEvent(
487
+ replacement.id,
488
+ { ownerEmail, accountEmail },
489
+ { scope: "single" },
490
+ );
491
+ } catch (cleanupError) {
492
+ console.error(
493
+ "Failed to clean up a working-location replacement after the original occurrence could not be cancelled.",
494
+ cleanupError,
495
+ );
496
+ }
497
+ throw error;
498
+ }
499
+ returnedGoogleEventId = replacement.id;
500
+ result = {
501
+ htmlLink: replacement.htmlLink,
502
+ meetLink: replacement.meetLink,
503
+ conferenceData: replacement.conferenceData,
504
+ };
505
+ } else {
506
+ result = await googleCalendar.updateEvent(googleEventId, updates, {
507
+ account: { ownerEmail, accountEmail },
508
+ sendUpdates:
509
+ args.sendUpdates ??
510
+ (guestNotificationMessage || (attendeesToAdd?.length ?? 0) > 0
511
+ ? "all"
512
+ : undefined),
513
+ addGoogleMeet: args.addGoogleMeet,
514
+ scope: args.scope,
515
+ });
516
+ }
309
517
 
310
518
  const returnedPatch: Partial<CalendarEvent> = {};
311
519
  if (result.htmlLink) returnedPatch.htmlLink = result.htmlLink;
@@ -340,7 +548,10 @@ export default defineAction({
340
548
 
341
549
  return {
342
550
  success: true,
343
- id: `google-${googleEventId}`,
551
+ id: `google-${returnedGoogleEventId}`,
552
+ ...(returnedGoogleEventId !== googleEventId
553
+ ? { replacedId: `google-${googleEventId}` }
554
+ : {}),
344
555
  accountEmail,
345
556
  updated: updatedKeys,
346
557
  htmlLink: result.htmlLink,