@agent-native/core 0.172.2 → 0.172.4
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/corpus/README.md +1 -1
- package/corpus/templates/calendar/actions/get-settings.ts +2 -5
- package/corpus/templates/calendar/actions/list-events.ts +21 -80
- package/corpus/templates/calendar/actions/update-settings.ts +11 -16
- package/corpus/templates/calendar/actions/view-screen.ts +25 -10
- package/corpus/templates/calendar/app/components/calendar/CommandPalette.tsx +7 -1
- package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +5 -2
- package/corpus/templates/calendar/app/lib/calendar-timezone.ts +3 -18
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +3 -62
- package/corpus/templates/calendar/changelog/2026-07-22-calendar-grid-local-time.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-19-the-calendar-loads-again-for-accounts-whose-saved-timezone-w.md +6 -0
- package/corpus/templates/calendar/server/handlers/settings.ts +8 -24
- package/corpus/templates/calendar/server/lib/calendar-settings.ts +58 -0
- package/corpus/templates/calendar/server/lib/find-time.ts +15 -93
- package/corpus/templates/calendar/shared/settings.ts +15 -9
- package/corpus/templates/calendar/shared/timezone.ts +127 -0
- package/corpus/templates/clips/actions/lib/finalize-ended-meetings.ts +37 -0
- package/corpus/templates/clips/actions/request-transcript.ts +4 -0
- package/corpus/templates/clips/actions/save-browser-transcript.ts +4 -0
- package/corpus/templates/clips/desktop/src/app.tsx +436 -365
- package/corpus/templates/clips/desktop/src/components/ReadinessPanel.tsx +17 -2
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +21 -2
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +17 -3
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +114 -20
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +130 -15
- package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -4
- package/corpus/templates/content/actions/_database-row-mutation.ts +22 -2
- package/corpus/templates/slides/actions/list-decks.ts +79 -5
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +21 -1
- package/corpus/templates/slides/app/components/editor/ShareDialog.tsx +49 -21
- package/corpus/templates/slides/app/context/DeckContext.tsx +49 -21
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +2 -2
- package/corpus/templates/slides/app/pages/Index.tsx +11 -13
- package/corpus/templates/slides/changelog/2026-08-24-deck-previews-and-sharing-live-in-the-overflow-menu.md +5 -0
- package/dist/agent/production-agent.js +5 -1
- package/dist/agent/types.d.ts +5 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/observability/posthog-ai.d.ts +36 -11
- package/dist/observability/posthog-ai.js +99 -30
- package/dist/observability/routes.js +9 -2
- package/dist/observability/traces.js +330 -142
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/tracking/providers.js +9 -3
- package/dist/vite/client.js +1 -0
- package/docs/content/observability.mdx +27 -10
- package/package.json +3 -3
|
@@ -3,6 +3,12 @@ import type {
|
|
|
3
3
|
FindTimeParticipant,
|
|
4
4
|
FindTimeSlot,
|
|
5
5
|
} from "../../shared/api.js";
|
|
6
|
+
import {
|
|
7
|
+
addDaysToDateKey,
|
|
8
|
+
dateKeyInTimezone,
|
|
9
|
+
dateTimeInTimezoneToIso,
|
|
10
|
+
isCalendarTimezone,
|
|
11
|
+
} from "../../shared/timezone.js";
|
|
6
12
|
|
|
7
13
|
export interface AvailabilitySchedule {
|
|
8
14
|
timezone: string;
|
|
@@ -37,108 +43,24 @@ const DEFAULT_SCHEDULE: AvailabilitySchedule["schedule"] = {
|
|
|
37
43
|
sunday: [],
|
|
38
44
|
};
|
|
39
45
|
|
|
46
|
+
/**
|
|
47
|
+
* These live in shared/timezone.ts so the grid, the actions, and this module
|
|
48
|
+
* cannot drift on DST edges. Kept under their original names because callers
|
|
49
|
+
* across the template import them from here.
|
|
50
|
+
*/
|
|
40
51
|
export function normalizeTimezone(timezone?: string): string {
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format();
|
|
44
|
-
return timezone;
|
|
45
|
-
} catch {
|
|
46
|
-
return "UTC";
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function datePartsInTimezone(date: Date, timezone: string) {
|
|
51
|
-
const parts = new Intl.DateTimeFormat("en-US", {
|
|
52
|
-
timeZone: timezone,
|
|
53
|
-
hourCycle: "h23",
|
|
54
|
-
year: "numeric",
|
|
55
|
-
month: "2-digit",
|
|
56
|
-
day: "2-digit",
|
|
57
|
-
hour: "2-digit",
|
|
58
|
-
minute: "2-digit",
|
|
59
|
-
second: "2-digit",
|
|
60
|
-
}).formatToParts(date);
|
|
61
|
-
const get = (type: string) =>
|
|
62
|
-
Number(parts.find((part) => part.type === type)?.value ?? "0");
|
|
63
|
-
return {
|
|
64
|
-
year: get("year"),
|
|
65
|
-
month: get("month"),
|
|
66
|
-
day: get("day"),
|
|
67
|
-
hour: get("hour"),
|
|
68
|
-
minute: get("minute"),
|
|
69
|
-
second: get("second"),
|
|
70
|
-
};
|
|
52
|
+
return isCalendarTimezone(timezone) ? timezone : "UTC";
|
|
71
53
|
}
|
|
72
54
|
|
|
73
|
-
export
|
|
74
|
-
|
|
75
|
-
return [
|
|
76
|
-
String(parts.year).padStart(4, "0"),
|
|
77
|
-
String(parts.month).padStart(2, "0"),
|
|
78
|
-
String(parts.day).padStart(2, "0"),
|
|
79
|
-
].join("-");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function addDaysToDateOnly(dateOnly: string, days: number): string {
|
|
83
|
-
const [year, month, day] = dateOnly.split("-").map(Number);
|
|
84
|
-
const date = new Date(Date.UTC(year, month - 1, day + days));
|
|
85
|
-
return [
|
|
86
|
-
String(date.getUTCFullYear()).padStart(4, "0"),
|
|
87
|
-
String(date.getUTCMonth() + 1).padStart(2, "0"),
|
|
88
|
-
String(date.getUTCDate()).padStart(2, "0"),
|
|
89
|
-
].join("-");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function offsetMsForTimezone(date: Date, timezone: string): number {
|
|
93
|
-
const parts = datePartsInTimezone(date, timezone);
|
|
94
|
-
const asUtc = Date.UTC(
|
|
95
|
-
parts.year,
|
|
96
|
-
parts.month - 1,
|
|
97
|
-
parts.day,
|
|
98
|
-
parts.hour,
|
|
99
|
-
parts.minute,
|
|
100
|
-
parts.second,
|
|
101
|
-
);
|
|
102
|
-
return asUtc - date.getTime();
|
|
103
|
-
}
|
|
55
|
+
export const dateOnlyInTimezone = dateKeyInTimezone;
|
|
56
|
+
export const addDaysToDateOnly = addDaysToDateKey;
|
|
104
57
|
|
|
105
58
|
export function zonedDateTimeToUtcIso(
|
|
106
59
|
dateOnly: string,
|
|
107
60
|
time: string,
|
|
108
61
|
timezone: string,
|
|
109
62
|
): string {
|
|
110
|
-
|
|
111
|
-
const [hour, minute] = time.split(":").map(Number);
|
|
112
|
-
const wallClockUtc = Date.UTC(year, month - 1, day, hour, minute, 0);
|
|
113
|
-
const offsets = new Set<number>();
|
|
114
|
-
for (let hours = -36; hours <= 36; hours += 6) {
|
|
115
|
-
offsets.add(
|
|
116
|
-
offsetMsForTimezone(
|
|
117
|
-
new Date(wallClockUtc + hours * 60 * 60 * 1000),
|
|
118
|
-
timezone,
|
|
119
|
-
),
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const candidates = [...offsets]
|
|
124
|
-
.map((offset) => new Date(wallClockUtc - offset))
|
|
125
|
-
.map((candidate) => ({
|
|
126
|
-
candidate,
|
|
127
|
-
localWallClock:
|
|
128
|
-
candidate.getTime() + offsetMsForTimezone(candidate, timezone),
|
|
129
|
-
}))
|
|
130
|
-
.sort((a, b) => {
|
|
131
|
-
const aDelta = a.localWallClock - wallClockUtc;
|
|
132
|
-
const bDelta = b.localWallClock - wallClockUtc;
|
|
133
|
-
if (aDelta === 0 && bDelta === 0) {
|
|
134
|
-
return a.candidate.getTime() - b.candidate.getTime();
|
|
135
|
-
}
|
|
136
|
-
if (aDelta >= 0 && bDelta < 0) return -1;
|
|
137
|
-
if (aDelta < 0 && bDelta >= 0) return 1;
|
|
138
|
-
return Math.abs(aDelta) - Math.abs(bDelta);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
return candidates[0].candidate.toISOString();
|
|
63
|
+
return dateTimeInTimezoneToIso(dateOnly, time, timezone);
|
|
142
64
|
}
|
|
143
65
|
|
|
144
66
|
function normalizeDateBound(value: string, timezone: string): string {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
DEFAULT_CALENDAR_WEEK_START,
|
|
4
4
|
isCalendarWeekStart,
|
|
5
5
|
} from "./calendar-week.js";
|
|
6
|
+
import { isCalendarTimezone } from "./timezone.js";
|
|
6
7
|
|
|
7
8
|
export const DEFAULT_SETTINGS: Settings = {
|
|
8
9
|
timezone: "America/New_York",
|
|
@@ -12,33 +13,38 @@ export const DEFAULT_SETTINGS: Settings = {
|
|
|
12
13
|
weekStart: DEFAULT_CALENDAR_WEEK_START,
|
|
13
14
|
};
|
|
14
15
|
|
|
15
|
-
export function normalizeCalendarSettings(
|
|
16
|
+
export function normalizeCalendarSettings(
|
|
17
|
+
input: unknown,
|
|
18
|
+
fallbacks?: Partial<Settings>,
|
|
19
|
+
): Settings {
|
|
20
|
+
const defaults = fallbacks
|
|
21
|
+
? normalizeCalendarSettings(fallbacks)
|
|
22
|
+
: DEFAULT_SETTINGS;
|
|
16
23
|
const raw =
|
|
17
24
|
input && typeof input === "object"
|
|
18
25
|
? (input as Partial<Settings>)
|
|
19
26
|
: ({} as Partial<Settings>);
|
|
20
27
|
|
|
21
28
|
return {
|
|
22
|
-
timezone:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
: DEFAULT_SETTINGS.timezone,
|
|
29
|
+
timezone: isCalendarTimezone(raw.timezone)
|
|
30
|
+
? raw.timezone
|
|
31
|
+
: defaults.timezone,
|
|
26
32
|
bookingPageTitle:
|
|
27
33
|
typeof raw.bookingPageTitle === "string"
|
|
28
34
|
? raw.bookingPageTitle
|
|
29
|
-
:
|
|
35
|
+
: defaults.bookingPageTitle,
|
|
30
36
|
bookingPageDescription:
|
|
31
37
|
typeof raw.bookingPageDescription === "string"
|
|
32
38
|
? raw.bookingPageDescription
|
|
33
|
-
:
|
|
39
|
+
: defaults.bookingPageDescription,
|
|
34
40
|
defaultEventDuration:
|
|
35
41
|
typeof raw.defaultEventDuration === "number" &&
|
|
36
42
|
Number.isFinite(raw.defaultEventDuration) &&
|
|
37
43
|
raw.defaultEventDuration > 0
|
|
38
44
|
? raw.defaultEventDuration
|
|
39
|
-
:
|
|
45
|
+
: defaults.defaultEventDuration,
|
|
40
46
|
weekStart: isCalendarWeekStart(raw.weekStart)
|
|
41
47
|
? raw.weekStart
|
|
42
|
-
:
|
|
48
|
+
: defaults.weekStart,
|
|
43
49
|
};
|
|
44
50
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constructing an `Intl.DateTimeFormat` costs ~45µs, and resolving one wall
|
|
3
|
+
* clock probes the zone a dozen times — so formatters are cached per zone and
|
|
4
|
+
* option set. Only the date varies per call, and that is an argument to
|
|
5
|
+
* `format`/`formatToParts`, never part of the formatter.
|
|
6
|
+
*/
|
|
7
|
+
const formatterCache = new Map<string, Intl.DateTimeFormat>();
|
|
8
|
+
|
|
9
|
+
export function timezoneFormatter(
|
|
10
|
+
timezone: string | undefined,
|
|
11
|
+
options: Intl.DateTimeFormatOptions,
|
|
12
|
+
locale?: string,
|
|
13
|
+
): Intl.DateTimeFormat {
|
|
14
|
+
const key = `${locale ?? ""}\u0000${timezone ?? ""}\u0000${JSON.stringify(options)}`;
|
|
15
|
+
let formatter = formatterCache.get(key);
|
|
16
|
+
if (!formatter) {
|
|
17
|
+
formatter = new Intl.DateTimeFormat(locale, {
|
|
18
|
+
...options,
|
|
19
|
+
timeZone: timezone,
|
|
20
|
+
});
|
|
21
|
+
formatterCache.set(key, formatter);
|
|
22
|
+
}
|
|
23
|
+
return formatter;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether a value names a zone this calendar can use. Only a `RangeError` means
|
|
28
|
+
* Intl rejected the zone; any other failure is a real fault and must surface
|
|
29
|
+
* instead of being reported as "invalid".
|
|
30
|
+
*
|
|
31
|
+
* Several older helpers around the template still run their own version of this
|
|
32
|
+
* check with a bare `catch`; prefer this one and delete those as you touch them.
|
|
33
|
+
*/
|
|
34
|
+
export function isCalendarTimezone(value: unknown): value is string {
|
|
35
|
+
if (typeof value !== "string" || !value.trim()) return false;
|
|
36
|
+
try {
|
|
37
|
+
new Intl.DateTimeFormat("en-US", { timeZone: value }).format();
|
|
38
|
+
return true;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (error instanceof RangeError) return false;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const OFFSET_PROBE_OPTIONS: Intl.DateTimeFormatOptions = {
|
|
46
|
+
hourCycle: "h23",
|
|
47
|
+
year: "numeric",
|
|
48
|
+
month: "2-digit",
|
|
49
|
+
day: "2-digit",
|
|
50
|
+
hour: "2-digit",
|
|
51
|
+
minute: "2-digit",
|
|
52
|
+
second: "2-digit",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function offsetMsInTimezone(date: Date, timezone: string): number {
|
|
56
|
+
const parts = timezoneFormatter(
|
|
57
|
+
timezone,
|
|
58
|
+
OFFSET_PROBE_OPTIONS,
|
|
59
|
+
"en-US",
|
|
60
|
+
).formatToParts(date);
|
|
61
|
+
const values = new Map(parts.map((part) => [part.type, part.value]));
|
|
62
|
+
const asUtc = Date.UTC(
|
|
63
|
+
Number(values.get("year")),
|
|
64
|
+
Number(values.get("month")) - 1,
|
|
65
|
+
Number(values.get("day")),
|
|
66
|
+
Number(values.get("hour")),
|
|
67
|
+
Number(values.get("minute")),
|
|
68
|
+
Number(values.get("second")),
|
|
69
|
+
);
|
|
70
|
+
return asUtc - date.getTime();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function dateTimeInTimezoneToIso(
|
|
74
|
+
date: string,
|
|
75
|
+
time: string,
|
|
76
|
+
timezone: string,
|
|
77
|
+
): string {
|
|
78
|
+
const [year, month, day] = date.split("-").map(Number);
|
|
79
|
+
const [hour, minute] = time.split(":").map(Number);
|
|
80
|
+
const wallClockUtc = Date.UTC(year, month - 1, day, hour, minute, 0);
|
|
81
|
+
const offsets = new Set<number>();
|
|
82
|
+
for (let hours = -36; hours <= 36; hours += 6) {
|
|
83
|
+
offsets.add(
|
|
84
|
+
offsetMsInTimezone(
|
|
85
|
+
new Date(wallClockUtc + hours * 60 * 60 * 1000),
|
|
86
|
+
timezone,
|
|
87
|
+
),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const candidates = [...offsets]
|
|
92
|
+
.map((offset) => new Date(wallClockUtc - offset))
|
|
93
|
+
.map((candidate) => ({
|
|
94
|
+
candidate,
|
|
95
|
+
localWallClock:
|
|
96
|
+
candidate.getTime() + offsetMsInTimezone(candidate, timezone),
|
|
97
|
+
}))
|
|
98
|
+
.sort((a, b) => {
|
|
99
|
+
const aDelta = a.localWallClock - wallClockUtc;
|
|
100
|
+
const bDelta = b.localWallClock - wallClockUtc;
|
|
101
|
+
if (aDelta === 0 && bDelta === 0) {
|
|
102
|
+
return a.candidate.getTime() - b.candidate.getTime();
|
|
103
|
+
}
|
|
104
|
+
if (aDelta >= 0 && bDelta < 0) return -1;
|
|
105
|
+
if (aDelta < 0 && bDelta >= 0) return 1;
|
|
106
|
+
return Math.abs(aDelta) - Math.abs(bDelta);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return candidates[0].candidate.toISOString();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function dateKeyInTimezone(date: Date, timezone: string): string {
|
|
113
|
+
const parts = timezoneFormatter(
|
|
114
|
+
timezone,
|
|
115
|
+
{ year: "numeric", month: "2-digit", day: "2-digit" },
|
|
116
|
+
"en-CA",
|
|
117
|
+
).formatToParts(date);
|
|
118
|
+
const value = (type: string) =>
|
|
119
|
+
parts.find((part) => part.type === type)!.value;
|
|
120
|
+
return `${value("year")}-${value("month")}-${value("day")}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function addDaysToDateKey(date: string, amount: number): string {
|
|
124
|
+
const [year, month, day] = date.split("-").map(Number);
|
|
125
|
+
const shifted = new Date(Date.UTC(year, month - 1, day + amount));
|
|
126
|
+
return shifted.toISOString().slice(0, 10);
|
|
127
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { and, eq, inArray, isNotNull } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
import { getDb, schema } from "../../server/db/index.js";
|
|
4
|
+
import finalizeMeeting from "../finalize-meeting.js";
|
|
5
|
+
|
|
6
|
+
/** Finalize meeting notes once a linked recording's transcript is ready. */
|
|
7
|
+
export async function finalizeEndedMeetingsForRecording(
|
|
8
|
+
db: ReturnType<typeof getDb>,
|
|
9
|
+
recordingId: string,
|
|
10
|
+
): Promise<void> {
|
|
11
|
+
const meetings = await db
|
|
12
|
+
.select({ id: schema.meetings.id })
|
|
13
|
+
.from(schema.meetings)
|
|
14
|
+
.where(
|
|
15
|
+
and(
|
|
16
|
+
eq(schema.meetings.recordingId, recordingId),
|
|
17
|
+
isNotNull(schema.meetings.actualEnd),
|
|
18
|
+
inArray(schema.meetings.transcriptStatus, ["ready", "failed"]),
|
|
19
|
+
eq(schema.meetings.summaryMd, ""),
|
|
20
|
+
),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
await Promise.all(
|
|
24
|
+
meetings.map(async (meeting) => {
|
|
25
|
+
try {
|
|
26
|
+
await finalizeMeeting.run({ meetingId: meeting.id });
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// Transcript delivery must remain successful even when the separate
|
|
29
|
+
// notes model is unavailable; a later transcript retry can try again.
|
|
30
|
+
console.warn(
|
|
31
|
+
`[clips] meeting notes finalization failed for ${meeting.id}:`,
|
|
32
|
+
(error as Error)?.message ?? String(error),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -80,6 +80,7 @@ import {
|
|
|
80
80
|
clearBuilderCreditsExhausted,
|
|
81
81
|
noteBuilderCreditsExhausted,
|
|
82
82
|
} from "./lib/builder-credits-state.js";
|
|
83
|
+
import { finalizeEndedMeetingsForRecording } from "./lib/finalize-ended-meetings.js";
|
|
83
84
|
import {
|
|
84
85
|
fetchLoomTranscript,
|
|
85
86
|
loomTranscriptUnavailableMessage,
|
|
@@ -739,6 +740,7 @@ export async function importLoomTranscriptForRecording({
|
|
|
739
740
|
now,
|
|
740
741
|
});
|
|
741
742
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
743
|
+
await finalizeEndedMeetingsForRecording(db, recordingId);
|
|
742
744
|
await queueBrainExport(recordingId);
|
|
743
745
|
return {
|
|
744
746
|
recordingId,
|
|
@@ -1177,6 +1179,7 @@ async function completeReadyTranscript({
|
|
|
1177
1179
|
// (`transcript-cleanup-${recordingId}`) before its next 2s tick lands —
|
|
1178
1180
|
// otherwise the "Cleaning up…" badge can lag for one full poll interval.
|
|
1179
1181
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
1182
|
+
await finalizeEndedMeetingsForRecording(db, recordingId);
|
|
1180
1183
|
await queueBrainExport(recordingId);
|
|
1181
1184
|
|
|
1182
1185
|
return {
|
|
@@ -1580,6 +1583,7 @@ const requestTranscriptAction = defineAction({
|
|
|
1580
1583
|
now,
|
|
1581
1584
|
});
|
|
1582
1585
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
1586
|
+
await finalizeEndedMeetingsForRecording(db, args.recordingId);
|
|
1583
1587
|
await queueBrainExport(args.recordingId);
|
|
1584
1588
|
await clearBuilderCreditsExhausted();
|
|
1585
1589
|
|
|
@@ -24,6 +24,7 @@ import { dispatchPostFinalizeJob } from "../server/lib/post-finalize-dispatch.js
|
|
|
24
24
|
import { getCurrentOwnerEmail } from "../server/lib/recordings.js";
|
|
25
25
|
import { buildCaptionSegmentsFromText } from "../shared/transcript-segments.js";
|
|
26
26
|
import { booleanParam } from "./lib/cli-params.js";
|
|
27
|
+
import { finalizeEndedMeetingsForRecording } from "./lib/finalize-ended-meetings.js";
|
|
27
28
|
import { isAutoTitleReplaceable } from "./lib/title-source.js";
|
|
28
29
|
|
|
29
30
|
// web-speech and macos-native are both mic-only engines — see
|
|
@@ -226,6 +227,9 @@ export default defineAction({
|
|
|
226
227
|
);
|
|
227
228
|
|
|
228
229
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
230
|
+
if (savedStatus === "ready") {
|
|
231
|
+
await finalizeEndedMeetingsForRecording(db, args.recordingId);
|
|
232
|
+
}
|
|
229
233
|
|
|
230
234
|
const [rec] = await db
|
|
231
235
|
.select({
|