























@@ -26,6 +26,8 @@ type ActiveMeetingNotesSession = {
2626const activeSessions = new Map<string, ActiveMeetingNotesSession>();
2727const AUTO_START_RETRY_ATTEMPTS = 12;
2828const AUTO_START_RETRY_MS = 5_000;
29+const AUTO_START_STOP_TIMEOUT_MS = 5_000;
30+const AUTO_START_PROVIDER_READY_TIMEOUT_MS = 30_000;
29313032function sameSessionIdentity(
3133left: MeetingNotesSessionDescriptor,
@@ -95,6 +97,28 @@ function createStore(api: OpenClawPluginApi): MeetingNotesStore {
9597return new MeetingNotesStore(path.join(api.runtime.state.resolveStateDir(), "meeting-notes"));
9698}
9799100+async function waitForPendingAutoStartsToSettle(
101+pendingStarts: Set<Promise<void>>,
102+): Promise<boolean> {
103+if (pendingStarts.size === 0) {
104+return true;
105+}
106+let timeout: ReturnType<typeof setTimeout> | undefined;
107+try {
108+return await Promise.race([
109+Promise.allSettled([...pendingStarts]).then(() => true),
110+new Promise<boolean>((resolve) => {
111+timeout = setTimeout(() => resolve(false), AUTO_START_STOP_TIMEOUT_MS);
112+timeout.unref?.();
113+}),
114+]);
115+} finally {
116+if (timeout) {
117+clearTimeout(timeout);
118+}
119+}
120+}
121+98122function sourceFromParams(params: Record<string, unknown>): MeetingNotesSourceLocator {
99123const providerId = readStringParam(params, "providerId", { trim: true }) ?? "manual-transcript";
100124return {
@@ -145,7 +169,12 @@ async function startMeetingNotes(params: {
145169api: OpenClawPluginApi;
146170store: MeetingNotesStore;
147171rawParams: Record<string, unknown>;
172+abortSignal?: AbortSignal;
173+startupWaitMs?: number;
148174}) {
175+if (params.abortSignal?.aborted) {
176+throw new Error("meeting notes start aborted");
177+}
149178const source = sourceFromParams(params.rawParams);
150179const provider = resolveSourceProvider(source.providerId, params.api);
151180if (!provider?.start) {
@@ -161,11 +190,22 @@ async function startMeetingNotes(params: {
161190const result = await provider.start({
162191cfg: params.api.config,
163192 session,
193+abortSignal: params.abortSignal,
194+startupWaitMs: params.startupWaitMs,
164195onUtterance: (utterance) => params.store.appendUtteranceForSession(session, utterance),
165196});
166197if (!result.ok) {
167198throw new Error(result.error);
168199}
200+if (params.abortSignal?.aborted) {
201+await provider.stop?.({
202+cfg: params.api.config,
203+sessionId: session.sessionId,
204+source: session.source,
205+reason: "service-stop",
206+});
207+throw new Error("meeting notes start aborted");
208+}
169209activeSessions.set(session.sessionId, { session, providerId: provider.id });
170210return toolText(`Meeting notes started: ${session.sessionId}`, {
171211sessionId: session.sessionId,
@@ -379,6 +419,8 @@ export function createMeetingNotesAutoStartService(api: OpenClawPluginApi): Open
379419let stopped = false;
380420const timers = new Set<ReturnType<typeof setTimeout>>();
381421const startedSessionIds = new Set<string>();
422+const pendingStartControllers = new Set<AbortController>();
423+const pendingStarts = new Set<Promise<void>>();
382424383425const schedule = (run: () => void, delayMs: number) => {
384426const timer = setTimeout(() => {
@@ -397,9 +439,13 @@ export function createMeetingNotesAutoStartService(api: OpenClawPluginApi): Open
397439if (stopped || !entry.enabled || startedSessionIds.has(entry.sessionId ?? "")) {
398440return;
399441}
400-void startMeetingNotes({
442+const abortController = new AbortController();
443+pendingStartControllers.add(abortController);
444+const startTask = startMeetingNotes({
401445api: serviceApi,
402446 store,
447+abortSignal: abortController.signal,
448+startupWaitMs: AUTO_START_PROVIDER_READY_TIMEOUT_MS,
403449rawParams: {
404450action: "start",
405451 ...entry,
@@ -413,7 +459,10 @@ export function createMeetingNotesAutoStartService(api: OpenClawPluginApi): Open
413459}
414460})
415461.catch((err) => {
416-if (stopped || attempt >= AUTO_START_RETRY_ATTEMPTS) {
462+if (stopped) {
463+return;
464+}
465+if (attempt >= AUTO_START_RETRY_ATTEMPTS) {
417466api.logger.warn(
418467`meeting-notes autoStart failed provider=${entry.providerId}: ${
419468 err instanceof Error ? err.message : String(err)
@@ -422,7 +471,12 @@ export function createMeetingNotesAutoStartService(api: OpenClawPluginApi): Open
422471return;
423472}
424473schedule(() => startEntry(entry, attempt + 1, serviceApi, store), AUTO_START_RETRY_MS);
474+})
475+.finally(() => {
476+pendingStartControllers.delete(abortController);
477+pendingStarts.delete(startTask);
425478});
479+pendingStarts.add(startTask);
426480};
427481428482return {
@@ -452,6 +506,17 @@ export function createMeetingNotesAutoStartService(api: OpenClawPluginApi): Open
452506clearTimeout(timer);
453507}
454508timers.clear();
509+for (const controller of pendingStartControllers) {
510+controller.abort();
511+}
512+const pendingStartsSettled = await waitForPendingAutoStartsToSettle(pendingStarts);
513+if (!pendingStartsSettled) {
514+api.logger.warn(
515+`meeting-notes autoStart stop timed out waiting for ${pendingStarts.size} pending start${
516+ pendingStarts.size === 1 ? "" : "s"
517+ }`,
518+);
519+}
455520const serviceApi = { ...api, config: ctx.config };
456521const store = new MeetingNotesStore(path.join(ctx.stateDir, "meeting-notes"));
457522for (const sessionId of startedSessionIds) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。