@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
@@ -1,6 +1,6 @@
1
1
  import { useT } from "@agent-native/core/client";
2
2
  import type { CalendarEvent } from "@shared/api";
3
- import { IconAlertTriangleFilled } from "@tabler/icons-react";
3
+ import { IconAlertTriangleFilled, IconMapPin } from "@tabler/icons-react";
4
4
  import {
5
5
  eachHourOfInterval,
6
6
  format,
@@ -28,6 +28,7 @@ import {
28
28
  useViewPreferences,
29
29
  type ViewPreferences,
30
30
  } from "@/hooks/use-view-preferences";
31
+ import { partitionAllDayEvents } from "@/lib/all-day-layout";
31
32
  import { getEventDisplayColor, allOtherDeclined } from "@/lib/event-colors";
32
33
  import {
33
34
  shouldSuppressAfterPopoverClose,
@@ -35,6 +36,12 @@ import {
35
36
  } from "@/lib/popover-click-guard";
36
37
  import { EventStatusIcon } from "@/lib/rsvp-status";
37
38
  import { cn } from "@/lib/utils";
39
+ import {
40
+ createWorkingLocationDisplayLabels,
41
+ getWorkingLocationChipLabel,
42
+ getWorkingLocationTitle,
43
+ isWorkingLocationEvent,
44
+ } from "@/lib/working-location";
38
45
 
39
46
  import { EventDetailPopover } from "./EventDetailPopover";
40
47
 
@@ -256,6 +263,8 @@ const DayEventCard = memo(function DayEventCard({
256
263
  onDraftCreate,
257
264
  onDraftDiscard,
258
265
  }: DayEventCardProps) {
266
+ const t = useT();
267
+ const workingLocationLabels = createWorkingLocationDisplayLabels(t);
259
268
  const li = layout.get(event.id) ?? {
260
269
  left: 0,
261
270
  width: 100,
@@ -323,10 +332,10 @@ const DayEventCard = memo(function DayEventCard({
323
332
  )}
324
333
  aria-label={
325
334
  event.ownerName || event.overlayEmail
326
- ? `${event.title}, ${
335
+ ? `${getWorkingLocationTitle(event, workingLocationLabels)}, ${
327
336
  event.ownerName || event.overlayEmail
328
337
  }'s calendar`
329
- : event.title
338
+ : getWorkingLocationTitle(event, workingLocationLabels)
330
339
  }
331
340
  style={{
332
341
  ...posStyle,
@@ -381,8 +390,13 @@ const DayEventCard = memo(function DayEventCard({
381
390
  !isPast && !isDeclined && "font-semibold",
382
391
  )}
383
392
  >
384
- {event.title}
393
+ {getWorkingLocationChipLabel(event, workingLocationLabels)}
385
394
  </span>
395
+ {isWorkingLocationEvent(event) && (
396
+ <span className="shrink-0 text-[10px] font-normal text-foreground/55">
397
+ {t("eventForm.workingLocation")}
398
+ </span>
399
+ )}
386
400
  </div>
387
401
  ) : (
388
402
  <>
@@ -403,8 +417,15 @@ const DayEventCard = memo(function DayEventCard({
403
417
  />
404
418
  )}
405
419
  <EventStatusIcon event={event} className="shrink-0" />
406
- <span className="truncate">{event.title}</span>
420
+ <span className="truncate">
421
+ {getWorkingLocationChipLabel(event, workingLocationLabels)}
422
+ </span>
407
423
  </div>
424
+ {isWorkingLocationEvent(event) && isStart && (
425
+ <div className="mt-0.5 truncate text-[10px] leading-tight text-foreground/60">
426
+ {t("eventForm.workingLocation")}
427
+ </div>
428
+ )}
408
429
  {isStart && (
409
430
  <div
410
431
  className={cn(
@@ -517,6 +538,7 @@ export const DayView = memo(function DayView({
517
538
  isLoading = false,
518
539
  }: DayViewProps) {
519
540
  const t = useT();
541
+ const workingLocationLabels = createWorkingLocationDisplayLabels(t);
520
542
  const { setFocusedEvent } = useCalendarSetters();
521
543
  const { prefs } = useViewPreferences();
522
544
  const [now, setNow] = useState(new Date());
@@ -568,6 +590,10 @@ export const DayView = memo(function DayView({
568
590
  );
569
591
 
570
592
  const allDayEvents = useMemo(() => events.filter((e) => e.allDay), [events]);
593
+ const { workingLocations, regularEvents: regularAllDayEvents } = useMemo(
594
+ () => partitionAllDayEvents(allDayEvents),
595
+ [allDayEvents],
596
+ );
571
597
  const timedEvents = useMemo(() => events.filter((e) => !e.allDay), [events]);
572
598
  const layout = useMemo(() => computeLayout(timedEvents), [timedEvents]);
573
599
 
@@ -726,72 +752,161 @@ export const DayView = memo(function DayView({
726
752
  </Tooltip>
727
753
  </div>
728
754
 
729
- {/* All-day events */}
755
+ {/* Working locations and ordinary all-day events */}
730
756
  {allDayEvents.length > 0 && (
731
- <div className="border-b border-border bg-card/50 px-4 py-2">
732
- <p className="mb-1.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
733
- {t("eventForm.allDay")}
734
- </p>
735
- <div className="space-y-1">
736
- {allDayEvents.map((event) => {
737
- const color = getEventDisplayColor(event, prefs);
738
- return (
739
- <EventDetailPopover
740
- key={event.id}
741
- event={event}
742
- onDelete={onDeleteEvent}
743
- isDraft={draftEventIds.includes(event.id)}
744
- defaultOpen={quickEditEventId === event.id}
745
- onTitleSave={onQuickEditSave}
746
- onDismissNew={onQuickEditCancel}
747
- onDraftUpdate={onDraftUpdate}
748
- onDraftCreate={onDraftCreate}
749
- onDraftDiscard={onDraftDiscard}
750
- >
751
- <button
752
- className={cn(
753
- "relative flex w-full items-center gap-1.5 rounded-md px-3 py-1.5 text-left text-sm font-medium text-foreground transition-all hover:brightness-110",
754
- event.ownerColor && "pr-5",
755
- )}
756
- aria-label={
757
- event.ownerName || event.overlayEmail
758
- ? `${event.title}, ${
759
- event.ownerName || event.overlayEmail
760
- }'s calendar`
761
- : event.title
762
- }
763
- style={
764
- color
765
- ? {
766
- backgroundColor: `${color}30`,
767
- borderLeft: `3px solid ${color}`,
768
- }
769
- : {
770
- backgroundColor: "hsl(var(--primary) / 0.15)",
771
- borderLeft: "3px solid hsl(var(--primary))",
772
- }
773
- }
774
- >
775
- {allOtherDeclined(event) && (
776
- <IconAlertTriangleFilled
777
- size={14}
778
- className="shrink-0 text-current opacity-70"
779
- />
780
- )}
781
- <EventStatusIcon event={event} className="shrink-0" />
782
- <span className="truncate">{event.title}</span>
783
- {event.ownerColor && (
784
- <span
785
- aria-hidden="true"
786
- className="absolute right-2 top-1/2 size-1.5 -translate-y-1/2 rounded-full ring-1 ring-background/70"
787
- style={{ backgroundColor: event.ownerColor }}
788
- />
789
- )}
790
- </button>
791
- </EventDetailPopover>
792
- );
793
- })}
794
- </div>
757
+ <div className="border-b border-border bg-card/50">
758
+ {workingLocations.length > 0 && (
759
+ <div data-working-location-lane className="px-4 py-1.5">
760
+ <p className="mb-1 flex items-center gap-1 text-[10px] font-medium uppercase text-muted-foreground">
761
+ <IconMapPin aria-hidden="true" className="size-3" />
762
+ {t("eventForm.workingLocation")}
763
+ </p>
764
+ <div className="grid gap-1 sm:grid-cols-2">
765
+ {workingLocations.map((event) => {
766
+ const color = getEventDisplayColor(event, prefs);
767
+ return (
768
+ <EventDetailPopover
769
+ key={`${event.overlayEmail ?? event.accountEmail ?? "primary"}:${event.id}`}
770
+ event={event}
771
+ onDelete={onDeleteEvent}
772
+ isDraft={draftEventIds.includes(event.id)}
773
+ defaultOpen={quickEditEventId === event.id}
774
+ onTitleSave={onQuickEditSave}
775
+ onDismissNew={onQuickEditCancel}
776
+ onDraftUpdate={onDraftUpdate}
777
+ onDraftCreate={onDraftCreate}
778
+ onDraftDiscard={onDraftDiscard}
779
+ >
780
+ <button
781
+ className={cn(
782
+ "relative flex h-6 w-full items-center gap-1.5 truncate rounded-sm px-2 text-left text-xs font-medium text-foreground transition-opacity hover:opacity-80",
783
+ event.ownerColor && "pr-5",
784
+ )}
785
+ aria-label={
786
+ event.ownerName || event.overlayEmail
787
+ ? `${getWorkingLocationTitle(event, workingLocationLabels)}, ${
788
+ event.ownerName || event.overlayEmail
789
+ }'s calendar`
790
+ : getWorkingLocationTitle(
791
+ event,
792
+ workingLocationLabels,
793
+ )
794
+ }
795
+ style={{
796
+ backgroundColor: color
797
+ ? `${color}1f`
798
+ : "hsl(var(--muted))",
799
+ borderLeft: `2px solid ${
800
+ color ?? "hsl(var(--muted-foreground))"
801
+ }`,
802
+ }}
803
+ >
804
+ <IconMapPin
805
+ aria-hidden="true"
806
+ className="size-3 shrink-0 opacity-70"
807
+ />
808
+ <span className="truncate">
809
+ {getWorkingLocationChipLabel(
810
+ event,
811
+ workingLocationLabels,
812
+ )}
813
+ </span>
814
+ {event.ownerColor && (
815
+ <span
816
+ aria-hidden="true"
817
+ className="absolute right-2 top-1/2 size-1.5 -translate-y-1/2 rounded-full ring-1 ring-background/70"
818
+ style={{ backgroundColor: event.ownerColor }}
819
+ />
820
+ )}
821
+ </button>
822
+ </EventDetailPopover>
823
+ );
824
+ })}
825
+ </div>
826
+ </div>
827
+ )}
828
+
829
+ {regularAllDayEvents.length > 0 && (
830
+ <div
831
+ data-all-day-event-lane
832
+ className={cn(
833
+ "px-4 py-2",
834
+ workingLocations.length > 0 && "border-t border-border/60",
835
+ )}
836
+ >
837
+ <p className="mb-1.5 text-[11px] font-medium uppercase text-muted-foreground">
838
+ {t("eventForm.allDay")}
839
+ </p>
840
+ <div className="flex flex-col gap-1">
841
+ {regularAllDayEvents.map((event) => {
842
+ const color = getEventDisplayColor(event, prefs);
843
+ return (
844
+ <EventDetailPopover
845
+ key={`${event.overlayEmail ?? event.accountEmail ?? "primary"}:${event.id}`}
846
+ event={event}
847
+ onDelete={onDeleteEvent}
848
+ isDraft={draftEventIds.includes(event.id)}
849
+ defaultOpen={quickEditEventId === event.id}
850
+ onTitleSave={onQuickEditSave}
851
+ onDismissNew={onQuickEditCancel}
852
+ onDraftUpdate={onDraftUpdate}
853
+ onDraftCreate={onDraftCreate}
854
+ onDraftDiscard={onDraftDiscard}
855
+ >
856
+ <button
857
+ className={cn(
858
+ "relative flex w-full items-center gap-1.5 rounded-md px-3 py-1.5 text-left text-sm font-medium text-foreground transition-all hover:brightness-110",
859
+ event.ownerColor && "pr-5",
860
+ )}
861
+ aria-label={
862
+ event.ownerName || event.overlayEmail
863
+ ? `${getWorkingLocationTitle(event, workingLocationLabels)}, ${
864
+ event.ownerName || event.overlayEmail
865
+ }'s calendar`
866
+ : getWorkingLocationTitle(
867
+ event,
868
+ workingLocationLabels,
869
+ )
870
+ }
871
+ style={
872
+ color
873
+ ? {
874
+ backgroundColor: `${color}30`,
875
+ borderLeft: `3px solid ${color}`,
876
+ }
877
+ : {
878
+ backgroundColor: "hsl(var(--primary) / 0.15)",
879
+ borderLeft: "3px solid hsl(var(--primary))",
880
+ }
881
+ }
882
+ >
883
+ {allOtherDeclined(event) && (
884
+ <IconAlertTriangleFilled
885
+ size={14}
886
+ className="shrink-0 text-current opacity-70"
887
+ />
888
+ )}
889
+ <EventStatusIcon event={event} className="shrink-0" />
890
+ <span className="truncate">
891
+ {getWorkingLocationChipLabel(
892
+ event,
893
+ workingLocationLabels,
894
+ )}
895
+ </span>
896
+ {event.ownerColor && (
897
+ <span
898
+ aria-hidden="true"
899
+ className="absolute right-2 top-1/2 size-1.5 -translate-y-1/2 rounded-full ring-1 ring-background/70"
900
+ style={{ backgroundColor: event.ownerColor }}
901
+ />
902
+ )}
903
+ </button>
904
+ </EventDetailPopover>
905
+ );
906
+ })}
907
+ </div>
908
+ </div>
909
+ )}
795
910
  </div>
796
911
  )}
797
912
 
@@ -1,3 +1,4 @@
1
+ import { useT } from "@agent-native/core/client";
1
2
  import type { CalendarEvent } from "@shared/api";
2
3
  import { IconAlertTriangleFilled } from "@tabler/icons-react";
3
4
 
@@ -8,6 +9,12 @@ import {
8
9
  } from "@/lib/event-colors";
9
10
  import { EventStatusIcon } from "@/lib/rsvp-status";
10
11
  import { cn } from "@/lib/utils";
12
+ import {
13
+ createWorkingLocationDisplayLabels,
14
+ getWorkingLocationChipLabel,
15
+ getWorkingLocationTitle,
16
+ isWorkingLocationEvent,
17
+ } from "@/lib/working-location";
11
18
 
12
19
  interface EventCardProps {
13
20
  event: CalendarEvent;
@@ -30,8 +37,13 @@ export function EventCard({
30
37
  dimmed = false,
31
38
  colorPreferences,
32
39
  }: EventCardProps) {
40
+ const t = useT();
41
+ const workingLocationLabels = createWorkingLocationDisplayLabels(t);
33
42
  const accentColor = getEventDisplayColor(event, colorPreferences);
34
43
  const ownerLabel = event.ownerName || event.overlayEmail;
44
+ const title = getWorkingLocationChipLabel(event, workingLocationLabels);
45
+ const ariaTitle = getWorkingLocationTitle(event, workingLocationLabels);
46
+ const isWorkingLocation = isWorkingLocationEvent(event);
35
47
 
36
48
  const handleDragStart = (e: React.DragEvent) => {
37
49
  e.dataTransfer.setData("text/plain", event.id);
@@ -55,7 +67,7 @@ export function EventCard({
55
67
  event.ownerColor && "pr-3.5",
56
68
  )}
57
69
  aria-label={
58
- ownerLabel ? `${event.title}, ${ownerLabel}'s calendar` : event.title
70
+ ownerLabel ? `${ariaTitle}, ${ownerLabel}'s calendar` : ariaTitle
59
71
  }
60
72
  style={{
61
73
  backgroundColor: `${accentColor}25`,
@@ -73,7 +85,12 @@ export function EventCard({
73
85
  />
74
86
  )}
75
87
  <EventStatusIcon event={event} />
76
- <span className="truncate font-medium">{event.title}</span>
88
+ <span className="truncate font-medium">{title}</span>
89
+ {isWorkingLocation && (
90
+ <span className="hidden shrink-0 text-[10px] font-normal text-foreground/65 sm:inline">
91
+ {t("eventForm.workingLocation")}
92
+ </span>
93
+ )}
77
94
  {event.ownerColor && (
78
95
  <span
79
96
  aria-hidden="true"
@@ -98,7 +115,7 @@ export function EventCard({
98
115
  event.ownerColor && "pr-4",
99
116
  )}
100
117
  aria-label={
101
- ownerLabel ? `${event.title}, ${ownerLabel}'s calendar` : event.title
118
+ ownerLabel ? `${ariaTitle}, ${ownerLabel}'s calendar` : ariaTitle
102
119
  }
103
120
  style={{
104
121
  backgroundColor: `${accentColor}25`,
@@ -113,8 +130,13 @@ export function EventCard({
113
130
  />
114
131
  )}
115
132
  <EventStatusIcon event={event} />
116
- <span className="truncate font-medium">{event.title}</span>
133
+ <span className="truncate font-medium">{title}</span>
117
134
  </div>
135
+ {isWorkingLocation && (
136
+ <span className="truncate text-foreground/70">
137
+ {t("eventForm.workingLocation")}
138
+ </span>
139
+ )}
118
140
  {!event.allDay && (
119
141
  <span className="text-foreground/70">
120
142
  {new Date(event.start).toLocaleTimeString([], {