@omnizap-system/omnizap 2.6.0 → 2.6.2
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/.env.example +58 -13
- package/.github/workflows/ci.yml +5 -5
- package/.github/workflows/codeql.yml +1 -1
- package/.github/workflows/db-migration-check.yml +2 -2
- package/.github/workflows/dependency-review.yml +1 -1
- package/.github/workflows/deploy.yml +2 -2
- package/.github/workflows/release.yml +2 -2
- package/.github/workflows/security-attest-provenance.yml +2 -2
- package/.github/workflows/security-gitleaks.yml +13 -4
- package/.github/workflows/security-runner-hardening.yml +2 -2
- package/.github/workflows/security-scorecard.yml +1 -1
- package/.github/workflows/security-zap-baseline.yml +1 -1
- package/.github/workflows/security-zap-full-scan.yml +2 -1
- package/.github/workflows/security-zizmor.yml +1 -1
- package/.github/workflows/wiki-sync.yml +1 -1
- package/.gitleaksignore +9 -0
- package/CODE_OF_CONDUCT.md +2 -2
- package/GEMINI.md +64 -0
- package/README.md +52 -82
- package/SECURITY.md +1 -1
- package/app/config/index.js +2 -0
- package/app/configParts/adminIdentity.js +5 -5
- package/app/configParts/baileysConfig.js +230 -58
- package/app/configParts/groupUtils.js +5 -0
- package/app/configParts/messagePersistenceService.js +145 -4
- package/app/configParts/sessionConfig.js +157 -0
- package/app/connection/baileysCompatibility.test.js +1 -1
- package/app/connection/groupOwnerWriteStateResolver.js +109 -0
- package/app/connection/socketController.js +660 -158
- package/app/connection/socketController.multiSession.test.js +108 -0
- package/app/controllers/messageController.js +1 -1
- package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
- package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
- package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +80 -2
- package/app/controllers/messageProcessingPipeline.js +93 -13
- package/app/controllers/messageProcessingPipeline.test.js +200 -0
- package/app/modules/adminModule/AGENT.md +1 -1
- package/app/modules/adminModule/commandConfig.json +3318 -1347
- package/app/modules/adminModule/groupCommandHandlers.js +858 -15
- package/app/modules/adminModule/groupCommandHandlers.test.js +378 -11
- package/app/modules/adminModule/groupWarningRepository.js +152 -0
- package/app/modules/aiModule/AGENT.md +47 -30
- package/app/modules/aiModule/aiConfigRuntime.js +1 -0
- package/app/modules/aiModule/catCommand.js +135 -27
- package/app/modules/aiModule/commandConfig.json +114 -28
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
- package/app/modules/gameModule/AGENT.md +1 -1
- package/app/modules/gameModule/commandConfig.json +29 -0
- package/app/modules/menuModule/AGENT.md +1 -1
- package/app/modules/menuModule/commandConfig.json +45 -10
- package/app/modules/menuModule/menuCatalogService.js +190 -0
- package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
- package/app/modules/menuModule/menuDynamicService.js +511 -0
- package/app/modules/menuModule/menuDynamicService.test.js +141 -0
- package/app/modules/menuModule/menus.js +36 -5
- package/app/modules/playModule/AGENT.md +10 -5
- package/app/modules/playModule/commandConfig.json +140 -12
- package/app/modules/playModule/playCommand.js +1 -1417
- package/app/modules/playModule/playCommandConstants.js +80 -0
- package/app/modules/playModule/playCommandCore.js +361 -0
- package/app/modules/playModule/playCommandHandlers.js +41 -0
- package/app/modules/playModule/playCommandMediaClient.js +1872 -0
- package/app/modules/playModule/playConfigRuntime.js +245 -4
- package/app/modules/playModule/playModuleCriticalFlows.test.js +152 -0
- package/app/modules/quoteModule/AGENT.md +1 -1
- package/app/modules/quoteModule/commandConfig.json +29 -0
- package/app/modules/quoteModule/quoteCommand.js +3 -2
- package/app/modules/rpgPokemonModule/AGENT.md +1 -1
- package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
- package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +5 -4
- package/app/modules/rpgPokemonModule/rpgBattleService.test.js +2 -1
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +2 -1
- package/app/modules/rpgPokemonModule/rpgPokemonService.js +38 -37
- package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +4 -3
- package/app/modules/statsModule/AGENT.md +1 -1
- package/app/modules/statsModule/commandConfig.json +58 -0
- package/app/modules/statsModule/rankingCommon.js +5 -4
- package/app/modules/stickerModule/AGENT.md +1 -1
- package/app/modules/stickerModule/addStickerMetadata.js +4 -3
- package/app/modules/stickerModule/commandConfig.json +145 -0
- package/app/modules/stickerModule/stickerCommand.js +1 -1
- package/app/modules/stickerPackModule/AGENT.md +1 -1
- package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
- package/app/modules/stickerPackModule/commandConfig.json +29 -0
- package/app/modules/stickerPackModule/semanticThemeClusterService.js +7 -6
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +10 -9
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +9 -8
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +3 -2
- package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +2 -1
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +80 -58
- package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +2 -1
- package/app/modules/stickerPackModule/stickerPackRepository.js +2 -1
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +5 -4
- package/app/modules/stickerPackModule/stickerPackService.js +13 -6
- package/app/modules/stickerPackModule/stickerStorageService.js +3 -2
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +2 -1
- package/app/modules/systemMetricsModule/AGENT.md +1 -1
- package/app/modules/systemMetricsModule/commandConfig.json +29 -0
- package/app/modules/systemMetricsModule/pingCommand.js +6 -5
- package/app/modules/tiktokModule/AGENT.md +1 -1
- package/app/modules/tiktokModule/commandConfig.json +29 -0
- package/app/modules/tiktokModule/tiktokCommand.js +2 -1
- package/app/modules/userModule/AGENT.md +1 -1
- package/app/modules/userModule/commandConfig.json +29 -0
- package/app/modules/userModule/userCommand.js +72 -23
- package/app/modules/waifuPicsModule/AGENT.md +57 -27
- package/app/modules/waifuPicsModule/commandConfig.json +87 -0
- package/app/modules/waifuPicsModule/waifuPicsCommand.js +3 -2
- package/app/observability/metrics.js +136 -0
- package/app/services/ai/commandConfigEnrichmentService.js +229 -47
- package/app/services/ai/conversationRouterService.js +4 -3
- package/app/services/ai/geminiService.js +132 -7
- package/app/services/ai/geminiService.test.js +59 -2
- package/app/services/ai/globalModuleAiHelpService.js +3 -2
- package/app/services/ai/messageCommandExecutionService.js +2 -1
- package/app/services/ai/moduleAiHelpCoreService.js +45 -14
- package/app/services/ai/moduleToolExecutorService.js +3 -2
- package/app/services/ai/moduleToolRegistryService.js +2 -1
- package/app/services/ai/toolCandidateSelectorService.js +6 -5
- package/app/services/auth/googleWebLinkService.js +3 -2
- package/app/services/auth/whatsappLoginLinkService.js +3 -2
- package/app/services/external/pokeApiService.js +4 -3
- package/app/services/group/groupMetadataService.js +24 -1
- package/app/services/infra/dbWriteQueue.js +57 -26
- package/app/services/infra/featureFlagService.js +2 -1
- package/app/services/messaging/captchaService.js +3 -2
- package/app/services/messaging/newsBroadcastService.js +846 -29
- package/app/services/multiSession/assignmentBalancerService.js +457 -0
- package/app/services/multiSession/groupOwnershipRepository.js +381 -0
- package/app/services/multiSession/groupOwnershipService.js +890 -0
- package/app/services/multiSession/groupOwnershipService.test.js +309 -0
- package/app/services/multiSession/sessionRegistryService.js +293 -0
- package/app/services/sticker/stickerFocusService.js +11 -10
- package/app/store/aiPromptStore.js +36 -19
- package/app/store/conversationSessionStore.js +7 -6
- package/app/store/groupConfigStore.js +41 -5
- package/app/store/premiumUserStore.js +21 -7
- package/app/utils/antiLink/antiLinkModule.js +352 -16
- package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
- package/app/workers/aiLearningWorker.js +6 -5
- package/app/workers/commandConfigEnrichmentWorker.js +4 -3
- package/database/index.js +14 -8
- package/database/migrations/20260307_d0_hardening_down.sql +1 -1
- package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
- package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
- package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
- package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
- package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
- package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
- package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
- package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
- package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
- package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
- package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
- package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
- package/database/schema.sql +102 -1
- package/docker-compose.yml +4 -1
- package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
- package/docs/compliance/dpa-b2b-standard-2026-03-07.md +1 -1
- package/docs/compliance/privacy-policy-2026-03-07.md +4 -4
- package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
- package/docs/security/incident-response-lgpd-anpd-runbook-2026-03-07.md +1 -1
- package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
- package/docs/security/omnizap-static-security-headers.conf +25 -0
- package/docs/wiki/Home.md +1 -1
- package/ecosystem.prod.config.cjs +32 -12
- package/index.js +57 -23
- package/observability/alert-rules.yml +20 -0
- package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
- package/observability/mysql-setup.sql +4 -4
- package/observability/system-admin-observability.md +26 -0
- package/package.json +20 -6
- package/public/apple-touch-icon.png +0 -0
- package/public/comandos/commands-catalog.json +2853 -3326
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/js/apps/apiDocsApp.js +3 -2
- package/public/js/apps/commandsReactApp.js +280 -99
- package/public/js/apps/createPackApp.js +11 -10
- package/public/js/apps/homeReactApp.js +181 -130
- package/public/js/apps/loginReactApp.js +1 -1
- package/public/js/apps/stickersApp.js +263 -110
- package/public/js/apps/termsReactApp.js +73 -24
- package/public/js/apps/userApp.js +4 -3
- package/public/js/apps/userPasswordResetReactApp.js +406 -0
- package/public/js/apps/userReactApp.js +355 -280
- package/public/js/apps/userSystemAdmReactApp.js +1506 -0
- package/public/pages/api-docs.html +1 -1
- package/public/pages/aup.html +2 -2
- package/public/pages/dpa.html +3 -3
- package/public/pages/licenca.html +4 -4
- package/public/pages/login.html +1 -1
- package/public/pages/notice-and-takedown.html +2 -2
- package/public/pages/politica-de-privacidade.html +6 -6
- package/public/pages/seo-bot-whatsapp-para-grupo.html +3 -3
- package/public/pages/seo-bot-whatsapp-sem-programar.html +3 -3
- package/public/pages/seo-como-automatizar-avisos-no-whatsapp.html +3 -3
- package/public/pages/seo-como-criar-comandos-whatsapp.html +3 -3
- package/public/pages/seo-como-evitar-spam-no-whatsapp.html +3 -3
- package/public/pages/seo-como-moderar-grupo-whatsapp.html +3 -3
- package/public/pages/seo-como-organizar-comunidade-whatsapp.html +3 -3
- package/public/pages/seo-melhor-bot-whatsapp-para-grupos.html +3 -3
- package/public/pages/stickers-admin.html +1 -1
- package/public/pages/stickers-create.html +1 -1
- package/public/pages/stickers.html +6 -6
- package/public/pages/suboperadores.html +2 -2
- package/public/pages/termos-de-uso-texto-integral.html +6 -6
- package/public/pages/termos-de-uso.html +3 -3
- package/public/pages/user-password-reset.html +4 -5
- package/public/pages/user-systemadm.html +9 -463
- package/public/pages/user.html +2 -2
- package/scripts/clear-whatsapp-session.sh +123 -0
- package/scripts/core-ai-mode.mjs +163 -0
- package/scripts/deploy.sh +11 -1
- package/scripts/email-broadcast-terms-update.mjs +2 -1
- package/scripts/enrich-command-config-ux-openai.mjs +492 -0
- package/scripts/generate-commands-catalog.mjs +166 -2
- package/scripts/generate-module-agents.mjs +2 -1
- package/scripts/generate-seo-satellite-pages.mjs +5 -4
- package/scripts/github-deploy-notify.mjs +2 -1
- package/scripts/github-release-notify.mjs +25 -10
- package/scripts/new-whatsapp-session.sh +317 -0
- package/scripts/release.sh +2 -19
- package/scripts/security-smoketest.mjs +6 -5
- package/scripts/security-web-surface-check.mjs +218 -0
- package/scripts/sticker-catalog-loadtest.mjs +5 -4
- package/server/auth/googleWebAuth/googleWebAuthService.js +8 -7
- package/server/auth/jwt/webJwtService.js +1 -1
- package/server/auth/stickerCatalogAuthContext.js +2 -1
- package/server/auth/termsAcceptance/termsAcceptanceHandler.js +2 -1
- package/server/auth/userPassword/userPasswordAuthService.js +2 -1
- package/server/auth/userPassword/userPasswordRecoveryService.js +4 -3
- package/server/auth/webAccount/webAccountHandlers.js +9 -10
- package/server/controllers/admin/adminPanelHandlers.js +267 -16
- package/server/controllers/admin/systemAdminController.js +267 -0
- package/server/controllers/seo/stickerCatalogSeoContext.js +10 -9
- package/server/controllers/sticker/nonCatalogHandlers.js +2 -1
- package/server/controllers/sticker/stickerCatalogController.js +23 -36
- package/server/controllers/system/contactController.js +9 -17
- package/server/controllers/system/githubController.js +3 -2
- package/server/controllers/system/stickerCatalogSystemContext.js +41 -19
- package/server/controllers/system/systemController.js +254 -1
- package/server/controllers/system/systemMetricsController.js +2 -1
- package/server/controllers/userController.js +6 -0
- package/server/email/emailTemplateService.js +5 -3
- package/server/http/httpServer.js +11 -6
- package/server/middleware/rateLimit.js +2 -1
- package/server/middleware/securityHeaders.js +20 -1
- package/server/routes/admin/systemAdminRouter.js +6 -0
- package/server/routes/indexRouter.js +30 -6
- package/server/routes/observability/grafanaProxyRouter.js +254 -0
- package/server/routes/static/staticPageRouter.js +27 -1
- package/server/utils/publicContact.js +31 -0
- package/utils/time/timeModule.js +135 -0
- package/utils/time/timeModule.test.js +65 -0
- package/utils/whatsapp/contactEnv.js +39 -0
- package/vite.config.mjs +7 -1
- package/public/assets/images/brand-icon-192.png +0 -0
- package/scripts/sync-readme-snapshot.mjs +0 -133
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
|
|
2
|
+
import logger from '#logger';
|
|
3
|
+
import { getAllToolRecords } from '../services/ai/moduleToolRegistryService.js';
|
|
4
|
+
import { applyCommandConfigEnrichmentSuggestion, saveCommandConfigEnrichmentSuggestion } from '../services/ai/commandConfigEnrichmentRepository.js';
|
|
5
|
+
import { generateCommandConfigEnrichmentSuggestion } from '../services/ai/commandConfigEnrichmentService.js';
|
|
6
|
+
import { upsertAiHelpCachedResponse } from '../services/ai/aiHelpResponseCacheRepository.js';
|
|
7
|
+
import { markToolCandidateCommandConfigCacheDirty } from '../services/ai/toolCandidateSelectorService.js';
|
|
8
|
+
|
|
9
|
+
const DEFAULT_INTERVAL_MS = 4 * 60 * 1000;
|
|
10
|
+
const DEFAULT_BATCH_SIZE = 12;
|
|
11
|
+
const DEFAULT_MIN_AUTO_APPLY_CONFIDENCE = 0.7;
|
|
12
|
+
const DEFAULT_MAX_HELP_QUESTIONS_PER_COMMAND = 3;
|
|
13
|
+
const DEFAULT_MAX_HELP_CALLS_PER_CYCLE = 42;
|
|
14
|
+
|
|
15
|
+
const parseEnvBool = (value, fallback) => {
|
|
16
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
17
|
+
const normalized = String(value).trim().toLowerCase();
|
|
18
|
+
if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) return true;
|
|
19
|
+
if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) return false;
|
|
20
|
+
return fallback;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const parseEnvInt = (value, fallback, min, max) => {
|
|
24
|
+
const parsed = Number.parseInt(String(value ?? ''), 10);
|
|
25
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
26
|
+
return Math.max(min, Math.min(max, parsed));
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const parseEnvFloat = (value, fallback, min, max) => {
|
|
30
|
+
const parsed = Number.parseFloat(String(value ?? ''));
|
|
31
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
32
|
+
return Math.max(min, Math.min(max, parsed));
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const AI_HELP_CONTINUOUS_LEARNING_ENABLED = parseEnvBool(process.env.AI_HELP_CONTINUOUS_LEARNING_ENABLED, true);
|
|
36
|
+
const AI_HELP_CONTINUOUS_LEARNING_INTERVAL_MS = parseEnvInt(process.env.AI_HELP_CONTINUOUS_LEARNING_INTERVAL_MS, DEFAULT_INTERVAL_MS, 45_000, 24 * 60 * 60 * 1000);
|
|
37
|
+
const AI_HELP_CONTINUOUS_LEARNING_BATCH_SIZE = parseEnvInt(process.env.AI_HELP_CONTINUOUS_LEARNING_BATCH_SIZE, DEFAULT_BATCH_SIZE, 1, 120);
|
|
38
|
+
const AI_HELP_CONTINUOUS_LEARNING_MIN_AUTO_APPLY_CONFIDENCE = parseEnvFloat(process.env.AI_HELP_CONTINUOUS_LEARNING_MIN_AUTO_APPLY_CONFIDENCE, DEFAULT_MIN_AUTO_APPLY_CONFIDENCE, 0.1, 0.99);
|
|
39
|
+
const AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_QUESTIONS_PER_COMMAND = parseEnvInt(process.env.AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_QUESTIONS_PER_COMMAND, DEFAULT_MAX_HELP_QUESTIONS_PER_COMMAND, 1, 12);
|
|
40
|
+
const AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_CALLS_PER_CYCLE = parseEnvInt(process.env.AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_CALLS_PER_CYCLE, DEFAULT_MAX_HELP_CALLS_PER_CYCLE, 1, 250);
|
|
41
|
+
|
|
42
|
+
let schedulerHandle = null;
|
|
43
|
+
let schedulerStarted = false;
|
|
44
|
+
let cycleInProgress = false;
|
|
45
|
+
let proactiveCursorIndex = 0;
|
|
46
|
+
let proactiveRound = 0;
|
|
47
|
+
let proactiveRegistrySignature = '';
|
|
48
|
+
|
|
49
|
+
const normalizeText = (value) =>
|
|
50
|
+
String(value || '')
|
|
51
|
+
.trim()
|
|
52
|
+
.toLowerCase()
|
|
53
|
+
.normalize('NFD')
|
|
54
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
55
|
+
.replace(/[^a-z0-9\s/_.-]/g, ' ')
|
|
56
|
+
.replace(/\s+/g, ' ')
|
|
57
|
+
.trim();
|
|
58
|
+
|
|
59
|
+
const normalizeDisplayText = (value) =>
|
|
60
|
+
String(value || '')
|
|
61
|
+
.trim()
|
|
62
|
+
.replace(/\s+/g, ' ');
|
|
63
|
+
|
|
64
|
+
const uniqueList = (items = [], limit = 8) => {
|
|
65
|
+
const output = [];
|
|
66
|
+
const seen = new Set();
|
|
67
|
+
for (const item of Array.isArray(items) ? items : []) {
|
|
68
|
+
const normalized = normalizeDisplayText(item);
|
|
69
|
+
if (!normalized) continue;
|
|
70
|
+
const dedupeKey = normalizeText(normalized);
|
|
71
|
+
if (!dedupeKey || seen.has(dedupeKey)) continue;
|
|
72
|
+
seen.add(dedupeKey);
|
|
73
|
+
output.push(normalized);
|
|
74
|
+
if (output.length >= limit) break;
|
|
75
|
+
}
|
|
76
|
+
return output;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const ensureArray = (value) => (Array.isArray(value) ? value : []);
|
|
80
|
+
const pickFirstText = (...values) => {
|
|
81
|
+
for (const value of values) {
|
|
82
|
+
const text = String(value ?? '').trim();
|
|
83
|
+
if (text) return text;
|
|
84
|
+
}
|
|
85
|
+
return '';
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const readCommandUsage = (entry = {}) => {
|
|
89
|
+
const usageV2 = ensureArray(entry?.usage);
|
|
90
|
+
if (usageV2.length) return usageV2;
|
|
91
|
+
const docsUsage = ensureArray(entry?.docs?.usage_examples);
|
|
92
|
+
if (docsUsage.length) return docsUsage;
|
|
93
|
+
return ensureArray(entry?.metodos_de_uso);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const readCommandFaqPatterns = (entry = {}) => {
|
|
97
|
+
const discovery = entry?.discovery && typeof entry.discovery === 'object' && !Array.isArray(entry.discovery) ? entry.discovery : {};
|
|
98
|
+
const source = ensureArray(discovery?.faq_queries).length ? discovery.faq_queries : entry?.faq_patterns;
|
|
99
|
+
return ensureArray(source);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const readCommandUserPhrasings = (entry = {}) => {
|
|
103
|
+
const discovery = entry?.discovery && typeof entry.discovery === 'object' && !Array.isArray(entry.discovery) ? entry.discovery : {};
|
|
104
|
+
const source = ensureArray(discovery?.user_phrasings).length ? discovery.user_phrasings : entry?.user_phrasings;
|
|
105
|
+
return ensureArray(source);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const readCommandDescription = (entry = {}) => pickFirstText(entry?.description, entry?.docs?.summary, entry?.descricao);
|
|
109
|
+
|
|
110
|
+
const readCommandPermission = (entry = {}) => pickFirstText(entry?.permission, entry?.permissao_necessaria);
|
|
111
|
+
|
|
112
|
+
const readCommandContexts = (entry = {}) => {
|
|
113
|
+
const contextsV2 = ensureArray(entry?.contexts);
|
|
114
|
+
if (contextsV2.length) return contextsV2;
|
|
115
|
+
return ensureArray(entry?.local_de_uso);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const readCommandUsageLimit = (entry = {}) => pickFirstText(entry?.limits?.usage_description, entry?.limite_de_uso);
|
|
119
|
+
|
|
120
|
+
const renderUsage = (method, commandPrefix = '/') => String(method || '').replaceAll('<prefix>', commandPrefix);
|
|
121
|
+
|
|
122
|
+
const computeRegistrySignature = (records = []) => records.map((record) => `${record?.toolName || ''}:${record?.moduleKey || ''}:${record?.commandName || ''}`).join('|');
|
|
123
|
+
|
|
124
|
+
const buildDeterministicExplainAnswer = ({ record, commandPrefix = '/' }) => {
|
|
125
|
+
const entry = record?.commandEntry && typeof record.commandEntry === 'object' ? record.commandEntry : {};
|
|
126
|
+
const commandName = String(record?.commandName || '').trim();
|
|
127
|
+
const commandToken = `${commandPrefix}${commandName}`;
|
|
128
|
+
const usage = readCommandUsage(entry).map((line) => renderUsage(line, commandPrefix));
|
|
129
|
+
const description = readCommandDescription(entry) || 'Sem descricao cadastrada.';
|
|
130
|
+
const permission = readCommandPermission(entry) || 'nao definido';
|
|
131
|
+
const contexts = readCommandContexts(entry);
|
|
132
|
+
const whereLabel = contexts.length ? contexts.join(', ') : 'nao definido';
|
|
133
|
+
const usageLimit = readCommandUsageLimit(entry) || 'nao informado';
|
|
134
|
+
|
|
135
|
+
const lines = [`Comando: ${commandToken}`, `Resumo: ${description}`, '', `Quem pode usar: ${permission}`, `Onde pode usar: ${whereLabel}`, `Limite: ${usageLimit}`, '', 'Como usar:', ...(usage.length ? usage.map((line) => `- ${line}`) : [`- ${commandToken}`]), '', 'Resposta pre-carregada em background para acelerar o IA Helper.'];
|
|
136
|
+
return lines.join('\n');
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const buildDeterministicQuestionAnswer = ({ question, record, commandPrefix = '/' }) => {
|
|
140
|
+
const entry = record?.commandEntry && typeof record.commandEntry === 'object' ? record.commandEntry : {};
|
|
141
|
+
const commandName = String(record?.commandName || '').trim();
|
|
142
|
+
const commandToken = `${commandPrefix}${commandName}`;
|
|
143
|
+
const usage = readCommandUsage(entry).map((line) => renderUsage(line, commandPrefix));
|
|
144
|
+
const description = readCommandDescription(entry) || 'Sem descricao cadastrada.';
|
|
145
|
+
const start = normalizeDisplayText(question);
|
|
146
|
+
|
|
147
|
+
return [start ? `Pergunta: ${start}` : `Comando: ${commandToken}`, '', `Resumo: ${description}`, usage.length ? `Exemplo: ${usage[0]}` : `Exemplo: ${commandToken}`, `Para detalhes completos, use: ${commandPrefix}help ${commandName}`].join('\n');
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const buildSyntheticEvent = ({ record, round = 0 }) => {
|
|
151
|
+
const entry = record?.commandEntry && typeof record.commandEntry === 'object' ? record.commandEntry : {};
|
|
152
|
+
const commandName = String(record?.commandName || '').trim();
|
|
153
|
+
const usage = readCommandUsage(entry);
|
|
154
|
+
const faq = readCommandFaqPatterns(entry);
|
|
155
|
+
const phrasings = readCommandUserPhrasings(entry);
|
|
156
|
+
|
|
157
|
+
const candidates = uniqueList([...phrasings, ...faq, ...usage, `como usar ${commandName}`, `o que faz ${commandName}`, `quando devo usar ${commandName}`, `me explica o comando ${commandName}`], 18);
|
|
158
|
+
|
|
159
|
+
const picked = candidates.length ? candidates[round % candidates.length] : `como usar ${commandName}`;
|
|
160
|
+
return {
|
|
161
|
+
id: null,
|
|
162
|
+
user_question: picked,
|
|
163
|
+
normalized_question: normalizeText(picked),
|
|
164
|
+
tool_suggested: record?.toolName || commandName,
|
|
165
|
+
tool_executed: record?.toolName || commandName,
|
|
166
|
+
success: true,
|
|
167
|
+
confidence: 0.92,
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const buildHelpWarmupQuestions = ({ record, syntheticEvent }) => {
|
|
172
|
+
const entry = record?.commandEntry && typeof record.commandEntry === 'object' ? record.commandEntry : {};
|
|
173
|
+
const commandName = String(record?.commandName || '').trim();
|
|
174
|
+
const usage = readCommandUsage(entry);
|
|
175
|
+
const faq = readCommandFaqPatterns(entry);
|
|
176
|
+
const phrasings = readCommandUserPhrasings(entry);
|
|
177
|
+
|
|
178
|
+
return uniqueList([syntheticEvent?.user_question || '', ...phrasings, ...faq, ...usage, `como usar /${commandName}`, `quero exemplo real de ${commandName}`, `o que eu recebo de resposta ao usar ${commandName}`], AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_QUESTIONS_PER_COMMAND);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const saveAiHelpSeedCacheEntries = async ({ record, syntheticEvent, maxEntries = 3 }) => {
|
|
182
|
+
const result = {
|
|
183
|
+
helpCalls: 0,
|
|
184
|
+
helpErrors: 0,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const commandName = String(record?.commandName || '').trim();
|
|
188
|
+
const moduleKey = String(record?.moduleKey || '').trim();
|
|
189
|
+
if (!commandName || !moduleKey) return result;
|
|
190
|
+
|
|
191
|
+
const explainQuestion = `explicar comando ${commandName}`;
|
|
192
|
+
const explainAnswer = buildDeterministicExplainAnswer({
|
|
193
|
+
record,
|
|
194
|
+
commandPrefix: '/',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
await upsertAiHelpCachedResponse({
|
|
199
|
+
moduleKey,
|
|
200
|
+
scope: 'command_explain',
|
|
201
|
+
question: explainQuestion,
|
|
202
|
+
normalizedQuestion: explainQuestion,
|
|
203
|
+
answer: explainAnswer,
|
|
204
|
+
source: 'continuous_seed',
|
|
205
|
+
commandName,
|
|
206
|
+
metadata: {
|
|
207
|
+
mode: 'continuous_learning',
|
|
208
|
+
reason: 'command_explain_seed',
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
result.helpCalls += 1;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
result.helpErrors += 1;
|
|
214
|
+
logger.warn('Falha ao persistir seed de command_explain no IA Helper continuo.', {
|
|
215
|
+
action: 'ai_helper_continuous_learning_cache_seed_failed',
|
|
216
|
+
module: moduleKey,
|
|
217
|
+
command: commandName,
|
|
218
|
+
error: error?.message,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const questions = buildHelpWarmupQuestions({
|
|
223
|
+
record,
|
|
224
|
+
syntheticEvent,
|
|
225
|
+
}).slice(0, Math.max(0, maxEntries - result.helpCalls));
|
|
226
|
+
|
|
227
|
+
for (const question of questions) {
|
|
228
|
+
try {
|
|
229
|
+
await upsertAiHelpCachedResponse({
|
|
230
|
+
moduleKey,
|
|
231
|
+
scope: 'question',
|
|
232
|
+
question,
|
|
233
|
+
normalizedQuestion: question,
|
|
234
|
+
answer: buildDeterministicQuestionAnswer({
|
|
235
|
+
question,
|
|
236
|
+
record,
|
|
237
|
+
commandPrefix: '/',
|
|
238
|
+
}),
|
|
239
|
+
source: 'continuous_seed',
|
|
240
|
+
commandName,
|
|
241
|
+
metadata: {
|
|
242
|
+
mode: 'continuous_learning',
|
|
243
|
+
reason: 'question_seed',
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
result.helpCalls += 1;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
result.helpErrors += 1;
|
|
249
|
+
logger.warn('Falha ao persistir seed de question no IA Helper continuo.', {
|
|
250
|
+
action: 'ai_helper_continuous_learning_cache_question_seed_failed',
|
|
251
|
+
module: moduleKey,
|
|
252
|
+
command: commandName,
|
|
253
|
+
question,
|
|
254
|
+
error: error?.message,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return result;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const selectProactiveBatch = () => {
|
|
263
|
+
const records = getAllToolRecords();
|
|
264
|
+
if (!records.length) {
|
|
265
|
+
proactiveCursorIndex = 0;
|
|
266
|
+
proactiveRound = 0;
|
|
267
|
+
proactiveRegistrySignature = '';
|
|
268
|
+
return {
|
|
269
|
+
records: [],
|
|
270
|
+
batch: [],
|
|
271
|
+
previousCursor: 0,
|
|
272
|
+
nextCursor: 0,
|
|
273
|
+
completedRound: false,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const signature = computeRegistrySignature(records);
|
|
278
|
+
if (signature !== proactiveRegistrySignature) {
|
|
279
|
+
proactiveRegistrySignature = signature;
|
|
280
|
+
proactiveCursorIndex = 0;
|
|
281
|
+
proactiveRound = 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const previousCursor = proactiveCursorIndex;
|
|
285
|
+
const safeBatchSize = Math.max(1, Math.min(AI_HELP_CONTINUOUS_LEARNING_BATCH_SIZE, records.length));
|
|
286
|
+
const batch = [];
|
|
287
|
+
for (let index = 0; index < safeBatchSize; index += 1) {
|
|
288
|
+
const cursor = (proactiveCursorIndex + index) % records.length;
|
|
289
|
+
batch.push(records[cursor]);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
proactiveCursorIndex = (proactiveCursorIndex + safeBatchSize) % records.length;
|
|
293
|
+
const completedRound = records.length > 0 && proactiveCursorIndex === 0;
|
|
294
|
+
if (completedRound) proactiveRound += 1;
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
records,
|
|
298
|
+
batch,
|
|
299
|
+
previousCursor,
|
|
300
|
+
nextCursor: proactiveCursorIndex,
|
|
301
|
+
completedRound,
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const processProactiveCommand = async ({ record, round = 0, helpCallBudget = Infinity }) => {
|
|
306
|
+
const syntheticEvent = buildSyntheticEvent({
|
|
307
|
+
record,
|
|
308
|
+
round,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const result = {
|
|
312
|
+
suggestionGenerated: 0,
|
|
313
|
+
suggestionApplied: 0,
|
|
314
|
+
suggestionChanged: 0,
|
|
315
|
+
helpCalls: 0,
|
|
316
|
+
helpErrors: 0,
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const suggestionOutput = await generateCommandConfigEnrichmentSuggestion({
|
|
320
|
+
learningEvent: syntheticEvent,
|
|
321
|
+
toolRecord: record,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
if (suggestionOutput?.suggestion) {
|
|
325
|
+
const savedSuggestion = await saveCommandConfigEnrichmentSuggestion({
|
|
326
|
+
moduleKey: record.moduleKey,
|
|
327
|
+
commandName: record.commandName,
|
|
328
|
+
sourceTool: record.toolName,
|
|
329
|
+
sourceEventId: null,
|
|
330
|
+
question: syntheticEvent.user_question,
|
|
331
|
+
normalizedQuestion: syntheticEvent.normalized_question,
|
|
332
|
+
suggestion: suggestionOutput.suggestion,
|
|
333
|
+
confidence: suggestionOutput.confidence,
|
|
334
|
+
modelName: suggestionOutput.modelName,
|
|
335
|
+
source: suggestionOutput.source,
|
|
336
|
+
status: 'pending',
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (savedSuggestion?.id) {
|
|
340
|
+
result.suggestionGenerated += 1;
|
|
341
|
+
const sourceValue = String(savedSuggestion.source || '');
|
|
342
|
+
const shouldAutoApply = savedSuggestion.confidence >= AI_HELP_CONTINUOUS_LEARNING_MIN_AUTO_APPLY_CONFIDENCE && sourceValue.startsWith('llm');
|
|
343
|
+
|
|
344
|
+
if (shouldAutoApply) {
|
|
345
|
+
const applyResult = await applyCommandConfigEnrichmentSuggestion({
|
|
346
|
+
suggestionId: savedSuggestion.id,
|
|
347
|
+
reviewNotes: `auto_apply_proactive: confidence>=${AI_HELP_CONTINUOUS_LEARNING_MIN_AUTO_APPLY_CONFIDENCE}`,
|
|
348
|
+
});
|
|
349
|
+
if (applyResult?.applied) {
|
|
350
|
+
result.suggestionApplied += 1;
|
|
351
|
+
if (applyResult.changed) result.suggestionChanged += 1;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (helpCallBudget <= 0) {
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const cacheSeedResult = await saveAiHelpSeedCacheEntries({
|
|
362
|
+
record,
|
|
363
|
+
syntheticEvent,
|
|
364
|
+
maxEntries: Math.max(1, helpCallBudget),
|
|
365
|
+
});
|
|
366
|
+
result.helpCalls += cacheSeedResult.helpCalls;
|
|
367
|
+
result.helpErrors += cacheSeedResult.helpErrors;
|
|
368
|
+
|
|
369
|
+
return result;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const processContinuousLearningBatch = async ({ reason = 'scheduler' } = {}) => {
|
|
373
|
+
if (cycleInProgress) return;
|
|
374
|
+
if (!AI_HELP_CONTINUOUS_LEARNING_ENABLED) return;
|
|
375
|
+
|
|
376
|
+
cycleInProgress = true;
|
|
377
|
+
const startedAt = __timeNowMs();
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
const selected = selectProactiveBatch();
|
|
381
|
+
if (!selected.batch.length) {
|
|
382
|
+
logger.info('Worker de aprendizado continuo IA sem comandos no registry.', {
|
|
383
|
+
action: 'ai_helper_continuous_learning_cycle_processed',
|
|
384
|
+
reason,
|
|
385
|
+
fetched_commands: 0,
|
|
386
|
+
generated_suggestions: 0,
|
|
387
|
+
applied_suggestions: 0,
|
|
388
|
+
changed_suggestions: 0,
|
|
389
|
+
help_calls: 0,
|
|
390
|
+
help_errors: 0,
|
|
391
|
+
duration_ms: __timeNowMs() - startedAt,
|
|
392
|
+
});
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
let generatedSuggestions = 0;
|
|
397
|
+
let appliedSuggestions = 0;
|
|
398
|
+
let changedSuggestions = 0;
|
|
399
|
+
let helpCalls = 0;
|
|
400
|
+
let helpErrors = 0;
|
|
401
|
+
let processedCommands = 0;
|
|
402
|
+
|
|
403
|
+
for (const record of selected.batch) {
|
|
404
|
+
if (!record) continue;
|
|
405
|
+
const remainingHelpBudget = AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_CALLS_PER_CYCLE - helpCalls;
|
|
406
|
+
if (remainingHelpBudget <= 0 && processedCommands > 0) {
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
const commandResult = await processProactiveCommand({
|
|
412
|
+
record,
|
|
413
|
+
round: proactiveRound,
|
|
414
|
+
helpCallBudget: Math.max(0, remainingHelpBudget),
|
|
415
|
+
});
|
|
416
|
+
generatedSuggestions += commandResult.suggestionGenerated;
|
|
417
|
+
appliedSuggestions += commandResult.suggestionApplied;
|
|
418
|
+
changedSuggestions += commandResult.suggestionChanged;
|
|
419
|
+
helpCalls += commandResult.helpCalls;
|
|
420
|
+
helpErrors += commandResult.helpErrors;
|
|
421
|
+
processedCommands += 1;
|
|
422
|
+
} catch (error) {
|
|
423
|
+
logger.warn('Falha ao processar comando no worker de aprendizado continuo IA.', {
|
|
424
|
+
action: 'ai_helper_continuous_learning_command_failed',
|
|
425
|
+
module: record?.moduleKey || null,
|
|
426
|
+
command: record?.commandName || null,
|
|
427
|
+
error: error?.message,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (changedSuggestions > 0) {
|
|
433
|
+
markToolCandidateCommandConfigCacheDirty();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
logger.info('Ciclo do worker de aprendizado continuo IA concluido.', {
|
|
437
|
+
action: 'ai_helper_continuous_learning_cycle_processed',
|
|
438
|
+
reason,
|
|
439
|
+
previous_cursor: selected.previousCursor,
|
|
440
|
+
next_cursor: selected.nextCursor,
|
|
441
|
+
completed_round: selected.completedRound,
|
|
442
|
+
round: proactiveRound,
|
|
443
|
+
registry_size: selected.records.length,
|
|
444
|
+
fetched_commands: selected.batch.length,
|
|
445
|
+
processed_commands: processedCommands,
|
|
446
|
+
generated_suggestions: generatedSuggestions,
|
|
447
|
+
applied_suggestions: appliedSuggestions,
|
|
448
|
+
changed_suggestions: changedSuggestions,
|
|
449
|
+
help_calls: helpCalls,
|
|
450
|
+
help_errors: helpErrors,
|
|
451
|
+
max_help_calls_per_cycle: AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_CALLS_PER_CYCLE,
|
|
452
|
+
duration_ms: __timeNowMs() - startedAt,
|
|
453
|
+
});
|
|
454
|
+
} finally {
|
|
455
|
+
cycleInProgress = false;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
export const startAiHelperContinuousLearningWorker = () => {
|
|
460
|
+
if (schedulerStarted) return;
|
|
461
|
+
|
|
462
|
+
if (!AI_HELP_CONTINUOUS_LEARNING_ENABLED) {
|
|
463
|
+
logger.info('Worker de aprendizado continuo IA desativado.', {
|
|
464
|
+
action: 'ai_helper_continuous_learning_worker_disabled',
|
|
465
|
+
enabled: AI_HELP_CONTINUOUS_LEARNING_ENABLED,
|
|
466
|
+
});
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
schedulerStarted = true;
|
|
471
|
+
void processContinuousLearningBatch({ reason: 'startup' });
|
|
472
|
+
|
|
473
|
+
schedulerHandle = setInterval(() => {
|
|
474
|
+
void processContinuousLearningBatch({ reason: 'scheduler' });
|
|
475
|
+
}, AI_HELP_CONTINUOUS_LEARNING_INTERVAL_MS);
|
|
476
|
+
if (typeof schedulerHandle?.unref === 'function') {
|
|
477
|
+
schedulerHandle.unref();
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
logger.info('Scheduler do worker de aprendizado continuo IA iniciado.', {
|
|
481
|
+
action: 'ai_helper_continuous_learning_worker_scheduler_started',
|
|
482
|
+
interval_ms: AI_HELP_CONTINUOUS_LEARNING_INTERVAL_MS,
|
|
483
|
+
batch_size: AI_HELP_CONTINUOUS_LEARNING_BATCH_SIZE,
|
|
484
|
+
min_auto_apply_confidence: AI_HELP_CONTINUOUS_LEARNING_MIN_AUTO_APPLY_CONFIDENCE,
|
|
485
|
+
max_help_questions_per_command: AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_QUESTIONS_PER_COMMAND,
|
|
486
|
+
max_help_calls_per_cycle: AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_CALLS_PER_CYCLE,
|
|
487
|
+
});
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
export const stopAiHelperContinuousLearningWorker = () => {
|
|
491
|
+
if (schedulerHandle) {
|
|
492
|
+
clearInterval(schedulerHandle);
|
|
493
|
+
schedulerHandle = null;
|
|
494
|
+
}
|
|
495
|
+
schedulerStarted = false;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
export const runAiHelperContinuousLearningWorkerOnce = async (reason = 'manual') => {
|
|
499
|
+
await processContinuousLearningBatch({
|
|
500
|
+
reason,
|
|
501
|
+
});
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
export const getAiHelperContinuousLearningWorkerConfig = () => ({
|
|
505
|
+
enabled: AI_HELP_CONTINUOUS_LEARNING_ENABLED,
|
|
506
|
+
intervalMs: AI_HELP_CONTINUOUS_LEARNING_INTERVAL_MS,
|
|
507
|
+
batchSize: AI_HELP_CONTINUOUS_LEARNING_BATCH_SIZE,
|
|
508
|
+
minAutoApplyConfidence: AI_HELP_CONTINUOUS_LEARNING_MIN_AUTO_APPLY_CONFIDENCE,
|
|
509
|
+
maxHelpQuestionsPerCommand: AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_QUESTIONS_PER_COMMAND,
|
|
510
|
+
maxHelpCallsPerCycle: AI_HELP_CONTINUOUS_LEARNING_MAX_HELP_CALLS_PER_CYCLE,
|
|
511
|
+
startedAt: __timeNowIso(),
|
|
512
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
|
|
1
2
|
import OpenAI from 'openai';
|
|
2
3
|
import logger from '#logger';
|
|
3
4
|
import { insertLearnedKeywords, insertLearnedPatterns, listPendingLearningEvents, markLearningEventsProcessed } from '../services/ai/aiLearningRepository.js';
|
|
@@ -305,7 +306,7 @@ const seedLearningFromCommandConfig = async ({ reason = 'scheduler' } = {}) => {
|
|
|
305
306
|
};
|
|
306
307
|
}
|
|
307
308
|
|
|
308
|
-
const nowMs =
|
|
309
|
+
const nowMs = __timeNowMs();
|
|
309
310
|
const registryStats = getToolRegistryStats();
|
|
310
311
|
const registrySignature = String(registryStats?.signature || '');
|
|
311
312
|
|
|
@@ -385,7 +386,7 @@ const processLearningBatch = async ({ reason = 'scheduler' } = {}) => {
|
|
|
385
386
|
|
|
386
387
|
cycleInProgress = true;
|
|
387
388
|
try {
|
|
388
|
-
const startedAt =
|
|
389
|
+
const startedAt = __timeNowMs();
|
|
389
390
|
logger.info('Worker de aprendizado IA iniciado.', {
|
|
390
391
|
action: 'ai_learning_worker_started',
|
|
391
392
|
reason,
|
|
@@ -433,7 +434,7 @@ const processLearningBatch = async ({ reason = 'scheduler' } = {}) => {
|
|
|
433
434
|
config_seed_executed: configSeedResult.executed,
|
|
434
435
|
config_seed_skipped: configSeedResult.skipped,
|
|
435
436
|
config_seed_tool_count: configSeedResult.toolCount,
|
|
436
|
-
duration_ms:
|
|
437
|
+
duration_ms: __timeNowMs() - startedAt,
|
|
437
438
|
});
|
|
438
439
|
return;
|
|
439
440
|
}
|
|
@@ -457,7 +458,7 @@ const processLearningBatch = async ({ reason = 'scheduler' } = {}) => {
|
|
|
457
458
|
config_seed_executed: configSeedResult.executed,
|
|
458
459
|
config_seed_skipped: configSeedResult.skipped,
|
|
459
460
|
config_seed_tool_count: configSeedResult.toolCount,
|
|
460
|
-
duration_ms:
|
|
461
|
+
duration_ms: __timeNowMs() - startedAt,
|
|
461
462
|
});
|
|
462
463
|
return;
|
|
463
464
|
}
|
|
@@ -537,7 +538,7 @@ const processLearningBatch = async ({ reason = 'scheduler' } = {}) => {
|
|
|
537
538
|
config_seed_executed: configSeedResult.executed,
|
|
538
539
|
config_seed_skipped: configSeedResult.skipped,
|
|
539
540
|
config_seed_tool_count: configSeedResult.toolCount,
|
|
540
|
-
duration_ms:
|
|
541
|
+
duration_ms: __timeNowMs() - startedAt,
|
|
541
542
|
});
|
|
542
543
|
} finally {
|
|
543
544
|
cycleInProgress = false;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
|
|
1
2
|
import logger from '#logger';
|
|
2
3
|
import { getToolRecord } from '../services/ai/moduleToolRegistryService.js';
|
|
3
4
|
import { applyCommandConfigEnrichmentSuggestion, getCommandConfigEnrichmentCursor, listLearningEventsForCommandConfigEnrichment, saveCommandConfigEnrichmentSuggestion, updateCommandConfigEnrichmentCursor } from '../services/ai/commandConfigEnrichmentRepository.js';
|
|
@@ -53,7 +54,7 @@ const processEnrichmentBatch = async ({ reason = 'scheduler' } = {}) => {
|
|
|
53
54
|
if (!isWorkerReady()) return;
|
|
54
55
|
|
|
55
56
|
cycleInProgress = true;
|
|
56
|
-
const startedAt =
|
|
57
|
+
const startedAt = __timeNowMs();
|
|
57
58
|
|
|
58
59
|
try {
|
|
59
60
|
logger.info('Worker de enriquecimento de commandConfig iniciado.', {
|
|
@@ -78,7 +79,7 @@ const processEnrichmentBatch = async ({ reason = 'scheduler' } = {}) => {
|
|
|
78
79
|
generated_suggestions: 0,
|
|
79
80
|
auto_applied: 0,
|
|
80
81
|
applied_changed: 0,
|
|
81
|
-
duration_ms:
|
|
82
|
+
duration_ms: __timeNowMs() - startedAt,
|
|
82
83
|
});
|
|
83
84
|
return;
|
|
84
85
|
}
|
|
@@ -186,7 +187,7 @@ const processEnrichmentBatch = async ({ reason = 'scheduler' } = {}) => {
|
|
|
186
187
|
auto_applied: autoApplied,
|
|
187
188
|
applied_changed: appliedChanged,
|
|
188
189
|
skipped_unknown_tool: skippedUnknownTool,
|
|
189
|
-
duration_ms:
|
|
190
|
+
duration_ms: __timeNowMs() - startedAt,
|
|
190
191
|
});
|
|
191
192
|
} finally {
|
|
192
193
|
cycleInProgress = false;
|
package/database/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
|
|
2
2
|
/* eslint-disable no-useless-escape */
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -102,9 +102,15 @@ export const TABLES = {
|
|
|
102
102
|
MESSAGE_ANALYSIS_EVENT: 'message_analysis_event',
|
|
103
103
|
BAILEYS_EVENT_JOURNAL: 'baileys_event_journal',
|
|
104
104
|
BAILEYS_AUTH_STATE: 'baileys_auth_state',
|
|
105
|
+
WA_SESSION_REGISTRY: 'wa_session_registry',
|
|
106
|
+
GROUP_ASSIGNMENT: 'group_assignment',
|
|
107
|
+
GROUP_ASSIGNMENT_HISTORY: 'group_assignment_history',
|
|
105
108
|
CHATS: 'chats',
|
|
106
109
|
GROUPS_METADATA: 'groups_metadata',
|
|
107
110
|
GROUP_CONFIGS: 'group_configs',
|
|
111
|
+
GROUP_USER_WARNINGS: 'group_user_warnings',
|
|
112
|
+
SYSTEM_PREMIUM_USERS: 'system_premium_users',
|
|
113
|
+
SYSTEM_AI_PROMPTS: 'system_ai_prompts',
|
|
108
114
|
LID_MAP: 'lid_map',
|
|
109
115
|
STICKER_PACK: 'sticker_pack',
|
|
110
116
|
STICKER_ASSET: 'sticker_asset',
|
|
@@ -408,8 +414,8 @@ let dbInFlightMetric = 0;
|
|
|
408
414
|
function createEmptyStats() {
|
|
409
415
|
return {
|
|
410
416
|
enabled: monitorConfig.enabled,
|
|
411
|
-
startedAt:
|
|
412
|
-
lastResetAt:
|
|
417
|
+
startedAt: __timeNowMs(),
|
|
418
|
+
lastResetAt: __timeNowMs(),
|
|
413
419
|
counters: {
|
|
414
420
|
total: 0,
|
|
415
421
|
error: 0,
|
|
@@ -632,7 +638,7 @@ function createDbMonitorLogger({ enabled, logPath, rotateBytes, keep }) {
|
|
|
632
638
|
} catch (error) {
|
|
633
639
|
queue.push(
|
|
634
640
|
JSON.stringify({
|
|
635
|
-
ts:
|
|
641
|
+
ts: __timeNowIso(),
|
|
636
642
|
event: 'logger_error',
|
|
637
643
|
errorMessage: error.message,
|
|
638
644
|
}),
|
|
@@ -678,7 +684,7 @@ export function resetDbStats() {
|
|
|
678
684
|
* @returns {object}
|
|
679
685
|
*/
|
|
680
686
|
export function getDbStats() {
|
|
681
|
-
const now =
|
|
687
|
+
const now = __timeNowMs();
|
|
682
688
|
const sampleCount = dbStats.samples.length;
|
|
683
689
|
const percentiles = calculatePercentiles();
|
|
684
690
|
const histogram = {
|
|
@@ -1219,7 +1225,7 @@ function recordStats({ fingerprint, normalizedSql, type, table, durationMs, ok,
|
|
|
1219
1225
|
entry.maxMs = Math.max(entry.maxMs, durationMs);
|
|
1220
1226
|
entry.minMs = entry.minMs === null ? durationMs : Math.min(entry.minMs, durationMs);
|
|
1221
1227
|
entry.lastMs = durationMs;
|
|
1222
|
-
entry.lastSeenAt =
|
|
1228
|
+
entry.lastSeenAt = __timeNowMs();
|
|
1223
1229
|
|
|
1224
1230
|
if (rowCount !== undefined) entry.lastRowCount = rowCount;
|
|
1225
1231
|
if (affectedRows !== undefined) entry.lastAffectedRows = affectedRows;
|
|
@@ -1232,7 +1238,7 @@ function recordStats({ fingerprint, normalizedSql, type, table, durationMs, ok,
|
|
|
1232
1238
|
*/
|
|
1233
1239
|
function buildMonitorLogEntry({ event, durationMs, type, table, fingerprint, normalizedSql, sql, rowCount, affectedRows, traceId, error, params }) {
|
|
1234
1240
|
const entry = {
|
|
1235
|
-
ts:
|
|
1241
|
+
ts: __timeNowIso(),
|
|
1236
1242
|
event,
|
|
1237
1243
|
durationMs: durationMs !== undefined && durationMs !== null ? Number(durationMs.toFixed(2)) : null,
|
|
1238
1244
|
type: type ?? null,
|
|
@@ -2058,7 +2064,7 @@ export async function upsert(tableName, data) {
|
|
|
2058
2064
|
* @param {(connection: import('mysql2/promise').PoolConnection) => Promise<any>} callback
|
|
2059
2065
|
* @returns {Promise<any>}
|
|
2060
2066
|
*/
|
|
2061
|
-
async function withTransaction(callback) {
|
|
2067
|
+
export async function withTransaction(callback) {
|
|
2062
2068
|
const connection = await pool.getConnection();
|
|
2063
2069
|
try {
|
|
2064
2070
|
await connection.beginTransaction();
|
|
@@ -58,7 +58,7 @@ UPDATE schema_change_log
|
|
|
58
58
|
SET status = 'rolled_back',
|
|
59
59
|
notes = 'D0 rollback executed',
|
|
60
60
|
updated_at = CURRENT_TIMESTAMP
|
|
61
|
-
WHERE migration_key = @migration_key;
|
|
61
|
+
WHERE migration_key = CONVERT(@migration_key USING utf8mb4) COLLATE utf8mb4_unicode_ci;
|
|
62
62
|
|
|
63
63
|
DROP PROCEDURE IF EXISTS __ensure_index;
|
|
64
64
|
DROP PROCEDURE IF EXISTS __drop_index_if_exists;
|
|
@@ -47,7 +47,7 @@ UPDATE schema_change_log
|
|
|
47
47
|
SET status = 'rolled_back',
|
|
48
48
|
notes = 'D+7 rollback executed',
|
|
49
49
|
updated_at = CURRENT_TIMESTAMP
|
|
50
|
-
WHERE migration_key = @migration_key;
|
|
50
|
+
WHERE migration_key = CONVERT(@migration_key USING utf8mb4) COLLATE utf8mb4_unicode_ci;
|
|
51
51
|
|
|
52
52
|
DROP PROCEDURE IF EXISTS __drop_column_if_exists;
|
|
53
53
|
DROP PROCEDURE IF EXISTS __drop_index_if_exists;
|