Files
memind/server/portal-integration-services-bootstrap.mjs
T
john dcf83e9b6f fix(memory-v2): improve pgvector recall and WeChat memory injection
Add keyword fallback and dedupe for pgvector resolve, score personal vs episodic memories by relevance, record WeChat recall product events, and disable broken chatrecall on Postgres session storage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 22:26:47 +08:00

361 lines
9.8 KiB
JavaScript

import {
resolveAnalyticsOwnerLabel,
resolveAnalyticsOwnerSegment,
resolveAnalyticsPlan,
sendMindSpaceAnalyticsEvent,
} from '../mindspace-analytics.mjs';
import {
sendMindSpaceRybbitEvent,
} from '../mindspace-rybbit.mjs';
import { createPageEditSessionService } from '../mindspace-page-edit-session.mjs';
import { createNotificationDispatcher } from '../notification-dispatcher.mjs';
import { startScheduleReminderWorker } from '../schedule-reminder-worker.mjs';
import { startScheduledTaskWorker } from '../scheduled-task-worker.mjs';
import { isPassiveCanaryRuntime } from './portal-runtime-role.mjs';
import { loadWechatMpModule } from '../wechat-mp-loader.mjs';
import { createToolGateway } from '../tool-gateway.mjs';
import {
createPageDataDeliveryCodeReviewService,
} from '../page-data-delivery-code-review.mjs';
export async function bootstrapPortalIntegrationServices({
pool,
h5Root,
codeRoot = h5Root,
env = process.env,
usersRoot,
wechatMpConfig,
mindSpacePublicFinish,
userAuth,
sessionAccess,
tkmindProxy,
scheduleService,
scheduledTaskService = null,
sessionSnapshotService = null,
wechatScheduleLlmConfigService,
wechatIntentRouter = null,
llmProviderService,
chatIntentRouter,
systemDisclosurePolicyService,
mindSpaceAnalyticsConfig,
mindSpaceRybbitConfig,
subscriptionService,
apiTarget,
apiSecret,
mindSpacePages,
mindSpacePageLiveEdit,
logger = console,
loadWechatMpModuleFn = loadWechatMpModule,
resolveAnalyticsOwnerSegmentFn =
resolveAnalyticsOwnerSegment,
resolveAnalyticsOwnerLabelFn =
resolveAnalyticsOwnerLabel,
resolveAnalyticsPlanFn = resolveAnalyticsPlan,
sendMindSpaceAnalyticsEventFn =
sendMindSpaceAnalyticsEvent,
sendMindSpaceRybbitEventFn =
sendMindSpaceRybbitEvent,
createNotificationDispatcherFn =
createNotificationDispatcher,
startScheduleReminderWorkerFn =
startScheduleReminderWorker,
startScheduledTaskWorkerFn =
startScheduledTaskWorker,
createPageEditSessionServiceFn =
createPageEditSessionService,
createToolGatewayFn = createToolGateway,
createPageDataDeliveryCodeReviewServiceFn =
createPageDataDeliveryCodeReviewService,
setIntervalFn = setInterval,
} = {}) {
if (
!pool ||
!h5Root ||
!usersRoot ||
!userAuth ||
!sessionAccess ||
!tkmindProxy ||
!llmProviderService
) {
throw new Error(
'bootstrapPortalIntegrationServices requires Portal integration dependencies',
);
}
const wechatMpRoot = codeRoot || h5Root;
logger.log?.(`[WeChat MP] Loading runtime module from code root: ${wechatMpRoot}`);
const wechatMp = await loadWechatMpModuleFn(wechatMpRoot);
const pageDataDeliveryReviewer =
wechatMpConfig?.pageDataAiderReviewEnabled === true
? createPageDataDeliveryCodeReviewServiceFn({
toolGateway: createToolGatewayFn({
llmProviderService,
env,
}),
userAuth,
timeoutMs: Number(
env.H5_WECHAT_MP_PAGE_DATA_AIDER_REVIEW_TIMEOUT_MS ??
15 * 60 * 1000,
),
logger,
})
: null;
const wechatMpService =
wechatMp.createWechatMpService({
config: wechatMpConfig,
userAuth,
sessionAccess,
htmlDeliveryAuthority:
typeof mindSpacePublicFinish
?.prepareWechatHtmlDelivery ===
'function' &&
typeof mindSpacePublicFinish
?.ensureWechatFreshPageThumbnails ===
'function'
? mindSpacePublicFinish
: null,
pageDataFinishGuard:
typeof mindSpacePublicFinish
?.prepareWechatPageDataDelivery ===
'function'
? mindSpacePublicFinish
: null,
pageDataDeliveryReviewer,
apiFetch: tkmindProxy.apiFetch,
startAgentSession: ({
userId,
workingDir,
sessionPolicy,
}) =>
tkmindProxy.startSessionForUser(userId, {
workingDir,
sessionPolicy,
origin: 'wechat',
}),
sessionApiFetch: async (
sessionId,
pathname,
init,
) => {
const target =
await tkmindProxy.resolveTarget(sessionId);
return tkmindProxy.apiFetchTo(
target,
pathname,
init,
);
},
submitSessionReply: ({
userId,
sessionId,
requestId,
userMessage,
options,
}) =>
tkmindProxy.submitSessionReplyForUser(
userId,
sessionId,
requestId,
userMessage,
options,
),
scheduleService:
env.H5_SCHEDULE_ENABLED === '1'
? scheduleService
: null,
wechatScheduleLlmConfigService,
llmProviderService,
chatIntentRouter,
wechatIntentRouter,
mysqlPool: pool,
systemDisclosurePolicyService,
sessionIntentClassifier: ({ text }) =>
chatIntentRouter?.classifySessionAction({
text,
}),
onPageGenerated: async ({
userId,
sessionId,
artifacts = [],
}) => {
for (const artifact of artifacts) {
const pageOwner =
(await userAuth
?.getUserById(userId)
.catch(() => null)) ?? {};
const analyticsPayload = {
eventName: 'page_generated',
ownerId: userId,
ownerSegment:
resolveAnalyticsOwnerSegmentFn(
pageOwner,
),
ownerLabel: resolveAnalyticsOwnerLabelFn(
pageOwner,
),
planType: resolveAnalyticsPlanFn(pageOwner),
generatedAt: new Date().toISOString(),
pageId: artifact.relativePath,
publicationId: sessionId,
agentRunId: sessionId,
channel: 'wechat_mp',
url:
artifact.url ||
artifact.relativePath ||
'/',
};
void sendMindSpaceAnalyticsEventFn({
config: mindSpaceAnalyticsConfig,
...analyticsPayload,
});
void sendMindSpaceRybbitEventFn({
config: mindSpaceRybbitConfig,
...analyticsPayload,
});
}
},
applySessionLlmProvider: (sessionId) =>
tkmindProxy.applySessionLlmProvider(sessionId),
refreshSessionSnapshot:
sessionSnapshotService?.isEnabled()
? (sessionId, userId) =>
sessionSnapshotService.refresh(
sessionId,
userId,
async (pathname, init) => {
const target =
await tkmindProxy.resolveTarget(
sessionId,
);
return tkmindProxy.apiFetchTo(
target,
pathname,
init,
);
},
)
: null,
});
const notificationDispatcher =
createNotificationDispatcherFn({
sendWechatTextToUser: wechatMpService?.enabled
? (userId, text) =>
wechatMpService.sendTextToUser(
userId,
text,
)
: null,
});
userAuth.setRechargeNotifier(
async ({ userId, title, body, dedupeKey }) => {
await notificationDispatcher.sendRechargeSuccess({
userId,
title,
body,
dedupeKey,
});
},
);
let scheduleReminderWorker = null;
if (
!isPassiveCanaryRuntime(env) &&
env.H5_REMINDER_WORKER_ENABLED === '1' &&
wechatMpService?.enabled &&
scheduleService
) {
scheduleReminderWorker =
startScheduleReminderWorkerFn({
scheduleService,
notificationDispatcher,
});
logger.log('Schedule reminder worker enabled');
}
let scheduledTaskWorker = null;
if (
!isPassiveCanaryRuntime(env) &&
env.H5_SCHEDULED_TASK_WORKER_ENABLED === '1' &&
scheduledTaskService &&
userAuth &&
tkmindProxy
) {
scheduledTaskWorker =
startScheduledTaskWorkerFn({
scheduledTaskService,
scheduleService,
userAuth,
tkmindProxy,
sessionSnapshotService,
notificationDispatcher,
pool,
h5Root,
logger,
});
logger.log('Scheduled task worker enabled');
}
let subscriptionExpiryTimer = null;
if (subscriptionService && !isPassiveCanaryRuntime(env)) {
subscriptionExpiryTimer = setIntervalFn(
async () => {
try {
const { renewed, failed } =
await subscriptionService.processAutoRenewals();
if (renewed > 0) {
logger.log(
`Auto-renewed ${renewed} subscription(s)`,
);
}
if (failed > 0) {
logger.log(
`Auto-renew failed for ${failed} subscription(s) (balance insufficient)`,
);
}
const expired =
await subscriptionService.expireStaleSubscriptions();
if (expired > 0) {
logger.log(
`Expired ${expired} stale subscription(s)`,
);
}
} catch (error) {
logger.warn(
'Subscription expiry check failed:',
error,
);
}
},
60 * 60 * 1000,
);
subscriptionExpiryTimer.unref?.();
}
const mindSpacePageEditSession =
createPageEditSessionServiceFn({
apiTarget,
apiSecret,
userAuth,
sessionAccess,
pageService: mindSpacePages,
pageLiveEdit: mindSpacePageLiveEdit,
llmProviderService,
});
if (wechatMpService?.enabled) {
logger.log('WeChat MP webhook enabled');
}
logger.log(
`User auth enabled (MySQL), workspace root: ${usersRoot}`,
);
return {
wechatMpService,
notificationDispatcher,
scheduleReminderWorker,
scheduledTaskWorker,
subscriptionExpiryTimer,
mindSpacePageEditSession,
};
}