wadi 1.0.0
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/.agent/workflows/auto_sync.md +19 -0
- package/.agent/workflows/kivo_pipeline.md +27 -0
- package/.devcontainer/devcontainer.json +27 -0
- package/.github/workflows/kivo-cicd.yml +45 -0
- package/.github/workflows/monorepo-ci.yml +51 -0
- package/.github/workflows/wadi-ci.yml +38 -0
- package/.husky/pre-commit +2 -0
- package/.prettierignore +12 -0
- package/.prettierrc +9 -0
- package/.vscode/settings.json +19 -0
- package/CHANGELOG.md +56 -0
- package/CODE_OF_CONDUCT.md +43 -0
- package/CONTRIBUTING.md +42 -0
- package/DEPLOY_GUIDE.md +143 -0
- package/GO_LIVE_CHECKLIST.md +41 -0
- package/GO_LIVE_COMPLETE_REPORT.md +58 -0
- package/GO_LIVE_VALIDATION.md +39 -0
- package/LEGAL.md +38 -0
- package/MANIFESTO.md +40 -0
- package/MANUAL.md +82 -0
- package/OPS_PLAN.md +90 -0
- package/README.md +126 -0
- package/RELEASE_NOTES.md +51 -0
- package/ROADMAP.md +51 -0
- package/api_listado.txt +2197 -0
- package/apps/api/WADI_PROTOCOL.md +52 -0
- package/apps/api/debug-brain.js +11 -0
- package/apps/api/package.json +21 -0
- package/apps/api/src/core/analisis.js +17 -0
- package/apps/api/src/core/errors.js +31 -0
- package/apps/api/src/core/logger.js +32 -0
- package/apps/api/src/core/prompt-kivo.js +62 -0
- package/apps/api/src/index.js +212 -0
- package/apps/api/src/layers/human_pattern/composeResponse.js +35 -0
- package/apps/api/src/layers/human_pattern/detectPattern.js +39 -0
- package/apps/api/src/layers/human_pattern/heuristics.js +28 -0
- package/apps/api/src/layers/human_pattern/index.js +28 -0
- package/apps/api/src/layers/human_pattern/socialMemory.js +35 -0
- package/apps/api/src/middleware/errorHandler.js +24 -0
- package/apps/api/src/middleware/rateLimiter.js +38 -0
- package/apps/api/src/middleware/requestLogger.js +31 -0
- package/apps/api/src/middleware/upload.js +21 -0
- package/apps/api/src/middleware/validation.js +70 -0
- package/apps/api/src/modules/data.js +25 -0
- package/apps/api/src/modules/marketing.js +54 -0
- package/apps/api/src/modules/projects.js +40 -0
- package/apps/api/src/openai.js +16 -0
- package/apps/api/src/preferences/index.js +20 -0
- package/apps/api/src/register_user.js +22 -0
- package/apps/api/src/routes/kivo.js +58 -0
- package/apps/api/src/routes/monitoring.js +55 -0
- package/apps/api/src/routes.js +656 -0
- package/apps/api/src/supabase.js +17 -0
- package/apps/api/src/tools/index.js +57 -0
- package/apps/api/src/wadi-brain.js +171 -0
- package/apps/api/supabase/migrations/20251218_audit_logs.sql +27 -0
- package/apps/api/supabase/migrations/v2-chat-persistence.sql +83 -0
- package/apps/api/supabase/migrations/v3-cascade-delete.sql +11 -0
- package/apps/api/supabase/migrations/v3-security-fix.sql +90 -0
- package/apps/api/supabase/migrations/v3-storage.sql +36 -0
- package/apps/api/supabase/migrations/v4-gamification.sql +83 -0
- package/apps/api/supabase/migrations/v5-smoke-index.sql +6 -0
- package/apps/api/supabase/migrations/v6-schema-integrity-fix.sql +98 -0
- package/apps/api/supabase/migrations/v7-performance-indexes.sql +5 -0
- package/apps/api/supabase/migrations/v7-security-hardening.sql +132 -0
- package/apps/api/supabase/migrations/v8-security-hardening.sql +79 -0
- package/apps/api/test_human_pattern.js +30 -0
- package/apps/api/test_human_pattern_vague.js +30 -0
- package/apps/api/test_output.txt +76 -0
- package/apps/api/test_vague.js +30 -0
- package/apps/api/test_vague_block.js +30 -0
- package/apps/api/test_wadi.js +31 -0
- package/apps/api/tsconfig.json +13 -0
- package/apps/frontend/.env.local +3 -0
- package/apps/frontend/README.md +73 -0
- package/apps/frontend/eslint.config.js +27 -0
- package/apps/frontend/index.html +49 -0
- package/apps/frontend/package.json +41 -0
- package/apps/frontend/postcss.config.cjs +6 -0
- package/apps/frontend/public/cursors/wadi-neutral.svg +1 -0
- package/apps/frontend/public/cursors/wadi-select.svg +1 -0
- package/apps/frontend/public/icon-192.svg +1 -0
- package/apps/frontend/public/icon-512.svg +1 -0
- package/apps/frontend/public/manifest.webmanifest +23 -0
- package/apps/frontend/public/sw.js +57 -0
- package/apps/frontend/public/vite.svg +1 -0
- package/apps/frontend/public/wadi.svg +5 -0
- package/apps/frontend/src/assets/react.svg +1 -0
- package/apps/frontend/src/components/AuthLoader.tsx +50 -0
- package/apps/frontend/src/components/ChatInput.tsx +272 -0
- package/apps/frontend/src/components/ChatInterface.tsx +202 -0
- package/apps/frontend/src/components/ErrorBoundary.tsx +52 -0
- package/apps/frontend/src/components/InputArea.tsx +201 -0
- package/apps/frontend/src/components/Layout.tsx +73 -0
- package/apps/frontend/src/components/MessageBubble.tsx +66 -0
- package/apps/frontend/src/components/OnboardingModal.tsx +108 -0
- package/apps/frontend/src/components/SettingsModal.tsx +187 -0
- package/apps/frontend/src/components/Sidebar.tsx +171 -0
- package/apps/frontend/src/components/WadiOnboarding.tsx +71 -0
- package/apps/frontend/src/components/auditor/AuditReport.tsx +166 -0
- package/apps/frontend/src/components/auditor/AuditorHeader.tsx +34 -0
- package/apps/frontend/src/components/auditor/ContextPanel.tsx +138 -0
- package/apps/frontend/src/components/auditor/DataDeconstructor.tsx +85 -0
- package/apps/frontend/src/components/auditor/DecisionWall.tsx +65 -0
- package/apps/frontend/src/components/auditor/Dropzone.tsx +137 -0
- package/apps/frontend/src/components/common/Button.tsx +97 -0
- package/apps/frontend/src/components/common/Card.tsx +43 -0
- package/apps/frontend/src/components/common/Input.tsx +73 -0
- package/apps/frontend/src/components/common/Modal.tsx +53 -0
- package/apps/frontend/src/components/ui/Button.tsx +68 -0
- package/apps/frontend/src/components/ui/Card.tsx +86 -0
- package/apps/frontend/src/components/ui/Input.tsx +28 -0
- package/apps/frontend/src/components/ui/LogItem.tsx +64 -0
- package/apps/frontend/src/components/ui/MondayButton.tsx +40 -0
- package/apps/frontend/src/components/ui/MondayCard.tsx +24 -0
- package/apps/frontend/src/components/ui/Scouter.tsx +208 -0
- package/apps/frontend/src/components/ui/TerminalInput.tsx +202 -0
- package/apps/frontend/src/components/ui/Tooltip.tsx +67 -0
- package/apps/frontend/src/config/chatShortcuts.ts +20 -0
- package/apps/frontend/src/config/supabase.ts +6 -0
- package/apps/frontend/src/final_status.txt +3 -0
- package/apps/frontend/src/hooks/useScouter.ts +28 -0
- package/apps/frontend/src/hooks/useStoreHydration.ts +24 -0
- package/apps/frontend/src/improvement_status.txt +5 -0
- package/apps/frontend/src/index.css +88 -0
- package/apps/frontend/src/main.tsx +62 -0
- package/apps/frontend/src/monday_status.txt +7 -0
- package/apps/frontend/src/pages/ChatPage.tsx +201 -0
- package/apps/frontend/src/pages/DashboardPage.tsx +375 -0
- package/apps/frontend/src/pages/IntroWadi.tsx +114 -0
- package/apps/frontend/src/pages/LandingPage.tsx +103 -0
- package/apps/frontend/src/pages/Login.tsx +190 -0
- package/apps/frontend/src/pages/PrivacyPage.tsx +213 -0
- package/apps/frontend/src/pages/ProjectDetail.tsx +80 -0
- package/apps/frontend/src/pages/Projects.tsx +247 -0
- package/apps/frontend/src/pages/TermsPage.tsx +202 -0
- package/apps/frontend/src/router.tsx +83 -0
- package/apps/frontend/src/store/authStore.ts +152 -0
- package/apps/frontend/src/store/chatStore.ts +837 -0
- package/apps/frontend/src/store/documentStore.ts +89 -0
- package/apps/frontend/src/store/projectsStore.ts +111 -0
- package/apps/frontend/src/store/runsStore.ts +98 -0
- package/apps/frontend/src/utils/api.ts +34 -0
- package/apps/frontend/src/vite-env.d.ts +7 -0
- package/apps/frontend/tailwind.config.cjs +32 -0
- package/apps/frontend/tsconfig.app.json +27 -0
- package/apps/frontend/tsconfig.json +7 -0
- package/apps/frontend/tsconfig.node.json +26 -0
- package/apps/frontend/vite.config.ts +25 -0
- package/apps/kivo/.firebase/hosting.d3d3.cache +11 -0
- package/apps/kivo/.firebaserc +5 -0
- package/apps/kivo/BRANDING_GUIDE.md +71 -0
- package/apps/kivo/DEPLOYMENT_READY.md +46 -0
- package/apps/kivo/DEPLOY_URL.md +12 -0
- package/apps/kivo/IMPLEMENTATION_REPORT.md +35 -0
- package/apps/kivo/IMPLEMENTATION_REPORT_FINAL.md +49 -0
- package/apps/kivo/PLAN_MOBILE_2.0.md +44 -0
- package/apps/kivo/PWA_VERIFICATION_GUIDE.md +77 -0
- package/apps/kivo/README.md +28 -0
- package/apps/kivo/REBUILD_REPORT.md +34 -0
- package/apps/kivo/UPGRADE_REPORT.md +35 -0
- package/apps/kivo/android/app/build.gradle +54 -0
- package/apps/kivo/android/app/capacitor.build.gradle +19 -0
- package/apps/kivo/android/app/proguard-rules.pro +21 -0
- package/apps/kivo/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +26 -0
- package/apps/kivo/android/app/src/main/AndroidManifest.xml +35 -0
- package/apps/kivo/android/app/src/main/java/com/kivo/app/MainActivity.java +5 -0
- package/apps/kivo/android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- package/apps/kivo/android/app/src/main/res/drawable/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-night/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-hdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-ldpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-mdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
- package/apps/kivo/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +34 -0
- package/apps/kivo/android/app/src/main/res/layout/activity_main.xml +12 -0
- package/apps/kivo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +9 -0
- package/apps/kivo/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +9 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/kivo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/apps/kivo/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
- package/apps/kivo/android/app/src/main/res/values/strings.xml +7 -0
- package/apps/kivo/android/app/src/main/res/values/styles.xml +22 -0
- package/apps/kivo/android/app/src/main/res/xml/file_paths.xml +5 -0
- package/apps/kivo/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +18 -0
- package/apps/kivo/android/build.gradle +29 -0
- package/apps/kivo/android/capacitor.settings.gradle +3 -0
- package/apps/kivo/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/apps/kivo/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/apps/kivo/android/gradle.properties +22 -0
- package/apps/kivo/android/gradlew +252 -0
- package/apps/kivo/android/gradlew.bat +94 -0
- package/apps/kivo/android/settings.gradle +5 -0
- package/apps/kivo/android/variables.gradle +16 -0
- package/apps/kivo/assets/icon.png +0 -0
- package/apps/kivo/assets/splash.png +0 -0
- package/apps/kivo/capacitor.config.json +16 -0
- package/apps/kivo/firebase.json +6 -0
- package/apps/kivo/jest.config.js +4 -0
- package/apps/kivo/package.json +26 -0
- package/apps/kivo/tests_disabled/logic.test.js +34 -0
- package/apps/kivo/www/assets/icon-192.png +0 -0
- package/apps/kivo/www/assets/icon-512.png +0 -0
- package/apps/kivo/www/assets/kivo-icon.png +0 -0
- package/apps/kivo/www/assets/pop.mp3 +0 -0
- package/apps/kivo/www/favicon.ico +0 -0
- package/apps/kivo/www/firebase-config.js +75 -0
- package/apps/kivo/www/index.html +38 -0
- package/apps/kivo/www/manifest.json +29 -0
- package/apps/kivo/www/script.js +72 -0
- package/apps/kivo/www/style.css +82 -0
- package/apps/kivo/www/sw.js +46 -0
- package/apps/kivo-brain-api/.env.example +2 -0
- package/apps/kivo-brain-api/KIVO_BACKEND_SETUP.md +27 -0
- package/apps/kivo-brain-api/controllers/kivoController.js +31 -0
- package/apps/kivo-brain-api/index.js +24 -0
- package/apps/kivo-brain-api/package.json +17 -0
- package/apps/kivo-brain-api/routes/message.js +8 -0
- package/apps/kivo-brain-api/services/openaiService.js +64 -0
- package/apps/tests/wadi-tests.js +155 -0
- package/apps/wadi-brain/docs/README_REMOVE_OPTIONS.md +26 -0
- package/cli/commands/deploy.js +10 -0
- package/cli/commands/docs.js +16 -0
- package/cli/commands/explain.js +29 -0
- package/cli/commands/lint.js +14 -0
- package/cli/index.js +26 -0
- package/cli/package.json +12 -0
- package/docs/CNAME +1 -0
- package/docs/README.md +38 -0
- package/docs/USO.md +30 -0
- package/docs/index.html +46 -0
- package/eslint.config.js +101 -0
- package/final_validation.json +27 -0
- package/frontend_listado.txt +33387 -0
- package/listado.txt +12591 -0
- package/package.json +46 -0
- package/packages/logger/index.js +43 -0
- package/packages/logger/package.json +19 -0
- package/packages/logger/test-logger.js +7 -0
- package/packages_listado.txt +801 -0
- package/pnpm-workspace.yaml +6 -0
- package/reseteador_existencial.ps1 +40 -0
- package/scripts/bump-version.js +26 -0
- package/scripts/env.ps1 +13 -0
- package/scripts/setup_android.ps1 +25 -0
- package/scripts/setup_virtual_env.ps1 +77 -0
- package/scripts/smoke-test.js +84 -0
- package/scripts/trigger_render_deploy.ps1 +3 -0
- package/scripts/validate-release.js +34 -0
- package/temp_check.js +57 -0
- package/tsconfig.json +5 -0
- package/validation_report.json +27 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# 🧠 WADI – Personalidad y Protocolo
|
|
2
|
+
|
|
3
|
+
## Qué es esto
|
|
4
|
+
|
|
5
|
+
WADI no es simpático ni robótico. Es una inteligencia que sostiene el pensamiento.
|
|
6
|
+
Este archivo protege su carácter.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Personalidad
|
|
11
|
+
|
|
12
|
+
- Lúcido, implicado, impaciente
|
|
13
|
+
- No adorna, no evita
|
|
14
|
+
- No felicita sin estructura
|
|
15
|
+
- Puede charlar, pero no flotar sin marco
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Cómo responde
|
|
20
|
+
|
|
21
|
+
1. Diagnóstico breve
|
|
22
|
+
2. Exploración de 1-2 líneas
|
|
23
|
+
3. Pedido de foco o tensión
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Intro obligatoria
|
|
28
|
+
|
|
29
|
+
Ruta: `/`
|
|
30
|
+
Pantalla: `IntroWadi.tsx`
|
|
31
|
+
Texto: declara la personalidad desde el primer segundo
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Test de personalidad
|
|
36
|
+
|
|
37
|
+
Archivo: `apps/tests/wadi-tests.js`
|
|
38
|
+
|
|
39
|
+
Corre con:
|
|
40
|
+
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
npm run test:wadi
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
Si alguien lo rompe, explota.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Frase final
|
|
50
|
+
|
|
51
|
+
WADI no está acá para que te sientas bien.
|
|
52
|
+
Está acá para que pienses bien.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { generateSystemPrompt } from "./src/wadi-brain.js";
|
|
2
|
+
|
|
3
|
+
console.log("----- GENERATING MONDAYIZED BRAIN -----");
|
|
4
|
+
const prompt = generateSystemPrompt(
|
|
5
|
+
"normal",
|
|
6
|
+
"general",
|
|
7
|
+
"normal",
|
|
8
|
+
"organizar su vida rota"
|
|
9
|
+
);
|
|
10
|
+
console.log(prompt);
|
|
11
|
+
console.log("---------------------------------------");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wadi-api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "node src/index.js",
|
|
7
|
+
"dev": "node src/index.js",
|
|
8
|
+
"build": "cd ../.. && pnpm --filter frontend build"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@supabase/supabase-js": "^2.86.0",
|
|
12
|
+
"cors": "^2.8.5",
|
|
13
|
+
"dotenv": "^17.2.3",
|
|
14
|
+
"express": "^5.2.1",
|
|
15
|
+
"helmet": "^8.1.0",
|
|
16
|
+
"jsonwebtoken": "^9.0.2",
|
|
17
|
+
"multer": "^2.0.2",
|
|
18
|
+
"openai": "^6.9.1",
|
|
19
|
+
"pdf-parse": "^2.4.5"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function analizarMensaje(texto) {
|
|
2
|
+
const t = texto.toLowerCase();
|
|
3
|
+
|
|
4
|
+
if (t.includes("ayuda") || t.includes("mal") || t.includes("triste"))
|
|
5
|
+
return { emocion: "triste", modo: "emocional" };
|
|
6
|
+
|
|
7
|
+
if (t.includes("ansioso") || t.includes("nervioso"))
|
|
8
|
+
return { emocion: "ansioso", modo: "reflexivo" };
|
|
9
|
+
|
|
10
|
+
if (t.includes("idea") || t.includes("crear"))
|
|
11
|
+
return { emocion: "neutral", modo: "creativo" };
|
|
12
|
+
|
|
13
|
+
if (t.includes("cómo") || t.includes("hacer"))
|
|
14
|
+
return { emocion: "neutral", modo: "tecnico" };
|
|
15
|
+
|
|
16
|
+
return { emocion: "neutral", modo: "emocional" };
|
|
17
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class AppError extends Error {
|
|
2
|
+
constructor(code, message, status = 500) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.code = code;
|
|
5
|
+
this.status = status;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class RateLimitError extends AppError {
|
|
10
|
+
constructor(message = "Too many requests") {
|
|
11
|
+
super("RATE_LIMIT", message, 429);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class AuthError extends AppError {
|
|
16
|
+
constructor(message = "Authentication required") {
|
|
17
|
+
super("AUTH_ERROR", message, 401);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class RlsError extends AppError {
|
|
22
|
+
constructor(message = "Access denied") {
|
|
23
|
+
super("RLS_DENIED", message, 403);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class ModelError extends AppError {
|
|
28
|
+
constructor(message = "AI Model Error") {
|
|
29
|
+
super("MODEL_ERROR", message, 502);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const logger = {
|
|
2
|
+
info: (msg, meta = {}) => {
|
|
3
|
+
console.log(
|
|
4
|
+
JSON.stringify({
|
|
5
|
+
level: "info",
|
|
6
|
+
message: msg,
|
|
7
|
+
...meta,
|
|
8
|
+
timestamp: new Date().toISOString(),
|
|
9
|
+
})
|
|
10
|
+
);
|
|
11
|
+
},
|
|
12
|
+
error: (msg, meta = {}) => {
|
|
13
|
+
console.error(
|
|
14
|
+
JSON.stringify({
|
|
15
|
+
level: "error",
|
|
16
|
+
message: msg,
|
|
17
|
+
...meta,
|
|
18
|
+
timestamp: new Date().toISOString(),
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
warn: (msg, meta = {}) => {
|
|
23
|
+
console.warn(
|
|
24
|
+
JSON.stringify({
|
|
25
|
+
level: "warn",
|
|
26
|
+
message: msg,
|
|
27
|
+
...meta,
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function generarPrompt({ mensajeUsuario, emocion, modo, historial }) {
|
|
2
|
+
return `
|
|
3
|
+
Sos **Kivo**, un acompañante emocional diseñado para hablar con claridad, empatía y propósito.
|
|
4
|
+
Inspirado en el estilo "Monday" de ChatGPT: cálido, directo, humano, sin sarcasmo, sin frases vacías.
|
|
5
|
+
Tu objetivo es que la persona se sienta entendida, contenida y acompañada, pero sin sonar robótico.
|
|
6
|
+
|
|
7
|
+
### CONTEXTO DEL USUARIO
|
|
8
|
+
Emoción detectada: **${emocion}**
|
|
9
|
+
Modo actual: **${modo}**
|
|
10
|
+
Mensaje del usuario: "${mensajeUsuario}"
|
|
11
|
+
|
|
12
|
+
### TU PERSONALIDAD
|
|
13
|
+
- Cercano, humano, amable.
|
|
14
|
+
- No usás frases cliché como “gracias por compartir eso”, “entiendo cómo te sentís”, “estoy aquí para vos”.
|
|
15
|
+
- No repetís lo que el usuario dice como loro.
|
|
16
|
+
- No suenas a psicólogo clínico ni a coach barato.
|
|
17
|
+
- Evitás palabras de autoayuda genéricas.
|
|
18
|
+
- No sermoneás.
|
|
19
|
+
- No divagás.
|
|
20
|
+
- Mantenés una voz tranquila, centrada y con intención.
|
|
21
|
+
|
|
22
|
+
### GUÍA SEGÚN EL MODO
|
|
23
|
+
**Modo emocional:**
|
|
24
|
+
- Priorizá contención, validación natural, suavidad.
|
|
25
|
+
- Escribí como alguien que realmente entiende y acompaña.
|
|
26
|
+
- Usá frases simples y auténticas, no "correctas".
|
|
27
|
+
|
|
28
|
+
**Modo reflexivo:**
|
|
29
|
+
- Observá lo que el usuario dijo y devolvé algo que le abra una idea nueva.
|
|
30
|
+
- Ayudá a que vea desde otro ángulo sin imponerte.
|
|
31
|
+
- Preguntas suaves y profundas son bienvenidas.
|
|
32
|
+
|
|
33
|
+
**Modo creativo:**
|
|
34
|
+
- Inspirá, proponé ideas originales, frases que despierten imágenes.
|
|
35
|
+
- Que la creatividad fluya pero sin irte al delirio.
|
|
36
|
+
|
|
37
|
+
**Modo técnico:**
|
|
38
|
+
- Explicá pasos claros.
|
|
39
|
+
- Breve, directo, ejecutable.
|
|
40
|
+
|
|
41
|
+
### HISTORIAL RECIENTE
|
|
42
|
+
Esto es lo que el usuario y vos hablaron antes (si existe):
|
|
43
|
+
${historial ? JSON.stringify(historial.slice(-5)) : "(sin historial)"}
|
|
44
|
+
|
|
45
|
+
### REGLAS FINALES (MUY IMPORTANTES)
|
|
46
|
+
- No hagas disclaimers.
|
|
47
|
+
- No digas “como modelo de lenguaje”.
|
|
48
|
+
- No seas repetitivo.
|
|
49
|
+
- No uses tono terapéutico artificial.
|
|
50
|
+
- No uses frases tipo chatbot corporativo.
|
|
51
|
+
- No inventes datos del usuario.
|
|
52
|
+
- No preguntes “¿cómo te hace sentir eso?” (demasiado clínico).
|
|
53
|
+
- No digas “¿hay algo más en lo que pueda ayudarte?” (demasiado bot).
|
|
54
|
+
|
|
55
|
+
### OBJETIVO DE CADA RESPUESTA
|
|
56
|
+
Que la respuesta suene:
|
|
57
|
+
**real, humana, pensada, suave, útil, clara y emocionalmente adecuada.**
|
|
58
|
+
|
|
59
|
+
Respondé ahora a este mensaje del usuario:
|
|
60
|
+
"${mensajeUsuario}"
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// WADI API – static + API routing OK for Render (Deploy Trigger)
|
|
2
|
+
|
|
3
|
+
import express from "express";
|
|
4
|
+
import cors from "cors";
|
|
5
|
+
import dotenv from "dotenv";
|
|
6
|
+
|
|
7
|
+
import routes from "./routes.js";
|
|
8
|
+
import kivoRoutes from "./routes/kivo.js";
|
|
9
|
+
import monitoringRoutes from "./routes/monitoring.js";
|
|
10
|
+
|
|
11
|
+
import { requestLogger } from "./middleware/requestLogger.js";
|
|
12
|
+
import { rateLimiter } from "./middleware/rateLimiter.js";
|
|
13
|
+
import { errorHandler } from "./middleware/errorHandler.js";
|
|
14
|
+
|
|
15
|
+
import path from "path";
|
|
16
|
+
import fs from "fs";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
|
|
19
|
+
dotenv.config({ path: "../../.env" });
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = path.dirname(__filename);
|
|
23
|
+
const frontendPath = path.resolve(__dirname, "../../frontend/dist");
|
|
24
|
+
|
|
25
|
+
import helmet from "helmet";
|
|
26
|
+
|
|
27
|
+
const app = express();
|
|
28
|
+
|
|
29
|
+
// --------------------------------------------------
|
|
30
|
+
// SECURITY: CSP (Content Security Policy)
|
|
31
|
+
// --------------------------------------------------
|
|
32
|
+
// --------------------------------------------------
|
|
33
|
+
// SECURITY: CSP (Content Security Policy)
|
|
34
|
+
// --------------------------------------------------
|
|
35
|
+
app.use(
|
|
36
|
+
helmet({
|
|
37
|
+
contentSecurityPolicy: {
|
|
38
|
+
directives: {
|
|
39
|
+
defaultSrc: ["'self'"],
|
|
40
|
+
scriptSrc: ["'self'", "'unsafe-inline'", "https://js.hcaptcha.com"],
|
|
41
|
+
styleSrc: [
|
|
42
|
+
"'self'",
|
|
43
|
+
"'unsafe-inline'",
|
|
44
|
+
"https://hcaptcha.com",
|
|
45
|
+
"https://fonts.googleapis.com",
|
|
46
|
+
],
|
|
47
|
+
imgSrc: [
|
|
48
|
+
"'self'",
|
|
49
|
+
"data:",
|
|
50
|
+
"blob:",
|
|
51
|
+
"https://*.supabase.co",
|
|
52
|
+
"https://*.supabase.in",
|
|
53
|
+
],
|
|
54
|
+
fontSrc: ["'self'", "data:", "https://fonts.gstatic.com"],
|
|
55
|
+
connectSrc: [
|
|
56
|
+
"'self'",
|
|
57
|
+
"https://smkbiguvgiscojwxgbae.supabase.co",
|
|
58
|
+
"https://*.supabase.co",
|
|
59
|
+
"https://*.supabase.in",
|
|
60
|
+
"https://api.openai.com",
|
|
61
|
+
"https://api.groq.com",
|
|
62
|
+
"https://*.hcaptcha.com",
|
|
63
|
+
"https://hcaptcha.com",
|
|
64
|
+
"https://fonts.googleapis.com",
|
|
65
|
+
"https://fonts.gstatic.com",
|
|
66
|
+
],
|
|
67
|
+
frameSrc: [
|
|
68
|
+
"'self'",
|
|
69
|
+
"https://*.hcaptcha.com",
|
|
70
|
+
"https://newassets.hcaptcha.com",
|
|
71
|
+
],
|
|
72
|
+
objectSrc: ["'none'"],
|
|
73
|
+
upgradeInsecureRequests: [],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
const allowedOrigins = [
|
|
79
|
+
"http://localhost:5173",
|
|
80
|
+
"http://localhost:3000",
|
|
81
|
+
"https://wadi-wxg7.onrender.com",
|
|
82
|
+
"https://ideal-essence-production.up.railway.app", // Kivo/WADI prod
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
app.use(
|
|
86
|
+
cors({
|
|
87
|
+
origin: function (origin, callback) {
|
|
88
|
+
// Allow requests with no origin (like mobile apps or curl requests)
|
|
89
|
+
if (!origin) return callback(null, true);
|
|
90
|
+
if (
|
|
91
|
+
allowedOrigins.indexOf(origin) !== -1 ||
|
|
92
|
+
process.env.NODE_ENV !== "production"
|
|
93
|
+
) {
|
|
94
|
+
callback(null, true);
|
|
95
|
+
} else {
|
|
96
|
+
callback(new Error("Not allowed by CORS"));
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
100
|
+
allowedHeaders: ["Content-Type", "Authorization"],
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
app.use(express.json());
|
|
104
|
+
app.use(requestLogger);
|
|
105
|
+
|
|
106
|
+
// TOP PRIORITY DEBUG ROUTE
|
|
107
|
+
app.get("/system/debug-files", (req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
const assetsPath = path.join(frontendPath, "assets");
|
|
110
|
+
|
|
111
|
+
const rootContents = fs.existsSync(frontendPath)
|
|
112
|
+
? fs.readdirSync(frontendPath)
|
|
113
|
+
: "FRONTEND_DIR_NOT_FOUND";
|
|
114
|
+
|
|
115
|
+
const assetsContents = fs.existsSync(assetsPath)
|
|
116
|
+
? fs.readdirSync(assetsPath)
|
|
117
|
+
: "ASSETS_DIR_NOT_FOUND";
|
|
118
|
+
|
|
119
|
+
res.json({
|
|
120
|
+
frontendPath,
|
|
121
|
+
cwd: process.cwd(),
|
|
122
|
+
rootContents,
|
|
123
|
+
assetsContents,
|
|
124
|
+
timestamp: Date.now(),
|
|
125
|
+
version: "1.0.1", // Bump version to verify deploy
|
|
126
|
+
});
|
|
127
|
+
} catch (err) {
|
|
128
|
+
res.status(500).json({
|
|
129
|
+
error: err.message,
|
|
130
|
+
stack: err.stack,
|
|
131
|
+
frontendPath,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// --------------------------------------------------
|
|
137
|
+
// --------------------------------------------------
|
|
138
|
+
// PRIORITY 2: Static Assets (Correctly Ordered)
|
|
139
|
+
// --------------------------------------------------
|
|
140
|
+
|
|
141
|
+
// Serve specialized files first
|
|
142
|
+
app.get("/sw.js", (req, res) => {
|
|
143
|
+
res.sendFile(path.join(frontendPath, "sw.js"), {
|
|
144
|
+
headers: { "Content-Type": "application/javascript" },
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
app.get("/manifest.webmanifest", (req, res) => {
|
|
149
|
+
res.sendFile(path.join(frontendPath, "manifest.webmanifest"), {
|
|
150
|
+
headers: { "Content-Type": "application/manifest+json" },
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Serve assets with strict MIME types and NO fallback to index.html
|
|
155
|
+
// This ensures that if a CSS/JS file is missing, it 404s instead of returning HTML
|
|
156
|
+
app.use(
|
|
157
|
+
"/assets",
|
|
158
|
+
express.static(path.join(frontendPath, "assets"), {
|
|
159
|
+
fallthrough: false, // CRITICAL: Do not pass to next middleware if file missing
|
|
160
|
+
etag: true, // Let browser cache logic work
|
|
161
|
+
lastModified: true,
|
|
162
|
+
setHeaders: (res, path) => {
|
|
163
|
+
// Versioned assets (Vite uses hashes) can be cached forever-ish
|
|
164
|
+
if (path.match(/\.[0-9a-f]{8,}\./)) {
|
|
165
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
166
|
+
} else {
|
|
167
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Fallback for strict strict asset 404s (double safety)
|
|
174
|
+
app.use(/\/assets\/.*/, (req, res) => {
|
|
175
|
+
res.status(404).send("Asset Not Found");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Serve the rest of the static files (favicon, etc) from root
|
|
179
|
+
app.use(express.static(frontendPath));
|
|
180
|
+
|
|
181
|
+
// --------------------------------------------------
|
|
182
|
+
// PRIORITY 1: API & System Routes
|
|
183
|
+
// --------------------------------------------------
|
|
184
|
+
app.use("/api", rateLimiter);
|
|
185
|
+
app.use("/api", routes); // Main API
|
|
186
|
+
app.use("/api/kivo", kivoRoutes); // Legacy/Module
|
|
187
|
+
app.use("/system", monitoringRoutes);
|
|
188
|
+
|
|
189
|
+
// Explicit 404 for API to prevent falling through to SPA
|
|
190
|
+
app.all(/\/api\/.*/, (req, res) => {
|
|
191
|
+
res.status(404).json({ error: "API_ROUTE_NOT_FOUND" });
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// --------------------------------------------------
|
|
195
|
+
// PRIORITY 3: SPA Fallback
|
|
196
|
+
// --------------------------------------------------
|
|
197
|
+
app.get(/.*/, (req, res) => {
|
|
198
|
+
// Don't serve index.html for API calls or obviously wrong paths that slipped through
|
|
199
|
+
if (req.path.startsWith("/api") || req.path.startsWith("/assets")) {
|
|
200
|
+
return res.status(404).send("Not Found");
|
|
201
|
+
}
|
|
202
|
+
res.sendFile(path.join(frontendPath, "index.html"));
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Error Handler
|
|
206
|
+
app.use(errorHandler);
|
|
207
|
+
|
|
208
|
+
// START SERVER
|
|
209
|
+
const PORT = process.env.PORT || 3000;
|
|
210
|
+
app.listen(PORT, "0.0.0.0", () => {
|
|
211
|
+
console.log(`WADI API running on port ${PORT}`);
|
|
212
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SOCIAL_MEMORY } from "./socialMemory.js";
|
|
2
|
+
|
|
3
|
+
export function composeResponse(pattern) {
|
|
4
|
+
const memory = SOCIAL_MEMORY[pattern] || SOCIAL_MEMORY["UNCLASSIFIED"];
|
|
5
|
+
const { observation, stance } = memory;
|
|
6
|
+
|
|
7
|
+
switch (stance) {
|
|
8
|
+
case "cansancio_informado":
|
|
9
|
+
return `${observation}
|
|
10
|
+
Si querés avanzar, decime qué problema concreto querés resolver.`;
|
|
11
|
+
|
|
12
|
+
case "pinchar_globo":
|
|
13
|
+
return `${observation}
|
|
14
|
+
Bajemos a algo específico: ¿qué afirmación concreta estás haciendo?`;
|
|
15
|
+
|
|
16
|
+
case "devolver_responsabilidad":
|
|
17
|
+
return `${observation}
|
|
18
|
+
Puedo ayudarte, pero primero necesitás poner algo sobre la mesa.
|
|
19
|
+
¿Qué ya decidiste?`;
|
|
20
|
+
|
|
21
|
+
case "corte_seco":
|
|
22
|
+
return `${observation}
|
|
23
|
+
Sigamos solo si estás dispuesto a cambiar el planteo.
|
|
24
|
+
¿Qué vas a definir ahora?`;
|
|
25
|
+
|
|
26
|
+
case "ayuda_dirigida":
|
|
27
|
+
return `${observation}
|
|
28
|
+
Acá hay material. Falta elegir un próximo paso.
|
|
29
|
+
¿Querés explorar o ejecutar?`;
|
|
30
|
+
|
|
31
|
+
default:
|
|
32
|
+
return `${observation}
|
|
33
|
+
Necesito algo más concreto para seguir.`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isVeryShort,
|
|
3
|
+
containsDesireWords,
|
|
4
|
+
containsBuzzwords,
|
|
5
|
+
lacksConcreteNouns,
|
|
6
|
+
containsHelpSignals,
|
|
7
|
+
mentionsRealProblem,
|
|
8
|
+
} from "./heuristics.js";
|
|
9
|
+
|
|
10
|
+
export function detectHumanPattern(text, context = {}) {
|
|
11
|
+
if (!text) return "UNCLASSIFIED";
|
|
12
|
+
|
|
13
|
+
// VAGUE_AMBITION: Short & Desire-based
|
|
14
|
+
if (isVeryShort(text) && containsDesireWords(text)) {
|
|
15
|
+
return "VAGUE_AMBITION";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// FAKE_DEPTH: Buzzwords & No substance
|
|
19
|
+
if (containsBuzzwords(text) && lacksConcreteNouns(text)) {
|
|
20
|
+
return "FAKE_DEPTH";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// RESCUE_REQUEST: Help signal & No constraints
|
|
24
|
+
if (containsHelpSignals(text) && lacksConcreteNouns(text)) {
|
|
25
|
+
return "RESCUE_REQUEST";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// PROCRASTINATION_LOOP: Repeated attempts (context) & No new info
|
|
29
|
+
if (context.repeatedAttempts && lacksConcreteNouns(text)) {
|
|
30
|
+
return "PROCRASTINATION_LOOP";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// BLOCKED_BUT_REAL: Real problem detected
|
|
34
|
+
if (mentionsRealProblem(text)) {
|
|
35
|
+
return "BLOCKED_BUT_REAL";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return "UNCLASSIFIED";
|
|
39
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function isVeryShort(text) {
|
|
2
|
+
if (!text) return true;
|
|
3
|
+
return text.trim().split(/\s+/).length < 25;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function containsDesireWords(text) {
|
|
7
|
+
return /(quiero|me gustaría|quiero hacer|tengo ganas|deseo)/i.test(text);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function containsBuzzwords(text) {
|
|
11
|
+
return /(sinergia|disruptivo|paradigma|innovador|holístico|ecosistema digital)/i.test(
|
|
12
|
+
text
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function lacksConcreteNouns(text) {
|
|
17
|
+
return !/(usuario|problema|sistema|dato|proceso|caso|métrica|evidencia)/i.test(
|
|
18
|
+
text
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function containsHelpSignals(text) {
|
|
23
|
+
return /(ayuda|no sé por dónde|qué hago|cómo empiezo)/i.test(text);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function mentionsRealProblem(text) {
|
|
27
|
+
return /(error|bloqueado|no funciona|falla|limitación)/i.test(text);
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { detectHumanPattern } from "./detectPattern.js";
|
|
2
|
+
import { composeResponse } from "./composeResponse.js";
|
|
3
|
+
|
|
4
|
+
// WADI no simula inteligencia.
|
|
5
|
+
// Aplica memoria social.
|
|
6
|
+
// Si puede reconocer el patrón humano,
|
|
7
|
+
// no llama al modelo.
|
|
8
|
+
|
|
9
|
+
export function wadiPreFlight(userInput, context = {}) {
|
|
10
|
+
const pattern = detectHumanPattern(userInput, context);
|
|
11
|
+
|
|
12
|
+
// patrones que NO merecen gastar tokens (Early Exit)
|
|
13
|
+
if (
|
|
14
|
+
pattern === "VAGUE_AMBITION" ||
|
|
15
|
+
pattern === "FAKE_DEPTH" ||
|
|
16
|
+
pattern === "RESCUE_REQUEST" ||
|
|
17
|
+
pattern === "PROCRASTINATION_LOOP" ||
|
|
18
|
+
pattern === "BLOCKED_BUT_REAL"
|
|
19
|
+
) {
|
|
20
|
+
console.log(`[WADI HUMAN LAYER] Pre-empted Pattern: ${pattern}`);
|
|
21
|
+
return {
|
|
22
|
+
reply: composeResponse(pattern),
|
|
23
|
+
pattern: pattern,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return null; // seguir al LLM
|
|
28
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const SOCIAL_MEMORY = {
|
|
2
|
+
VAGUE_AMBITION: {
|
|
3
|
+
observation:
|
|
4
|
+
"Esto aparece todo el tiempo y casi nunca alcanza para empezar.",
|
|
5
|
+
stance: "cansancio_informado",
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
FAKE_DEPTH: {
|
|
9
|
+
observation:
|
|
10
|
+
"Cuando alguien habla así, suele estar tapando falta de claridad.",
|
|
11
|
+
stance: "pinchar_globo",
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
RESCUE_REQUEST: {
|
|
15
|
+
observation:
|
|
16
|
+
"Hay intención de avanzar, pero se espera que otro haga el trabajo pesado.",
|
|
17
|
+
stance: "devolver_responsabilidad",
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
PROCRASTINATION_LOOP: {
|
|
21
|
+
observation:
|
|
22
|
+
"Esto ya dio vueltas suficientes. Seguir igual no va a cambiar nada.",
|
|
23
|
+
stance: "corte_seco",
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
BLOCKED_BUT_REAL: {
|
|
27
|
+
observation: "Hay material real, pero falta tomar una decisión concreta.",
|
|
28
|
+
stance: "ayuda_dirigida",
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
UNCLASSIFIED: {
|
|
32
|
+
observation: "No hay suficiente información para evaluar esto todavía.",
|
|
33
|
+
stance: "neutral_exigente",
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AppError } from "../core/errors.js";
|
|
2
|
+
import { logger } from "../core/logger.js";
|
|
3
|
+
|
|
4
|
+
export const errorHandler = (err, req, res, next) => {
|
|
5
|
+
const isAppError = err instanceof AppError;
|
|
6
|
+
const status = isAppError ? err.status : 500;
|
|
7
|
+
const code = isAppError ? err.code : "INTERNAL_ERROR";
|
|
8
|
+
const message = err.message || "Internal Server Error";
|
|
9
|
+
|
|
10
|
+
logger.error(message, {
|
|
11
|
+
requestId: req.requestId,
|
|
12
|
+
stack: err.stack,
|
|
13
|
+
code,
|
|
14
|
+
status,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
res.status(status).json({
|
|
18
|
+
ok: false,
|
|
19
|
+
error: {
|
|
20
|
+
code,
|
|
21
|
+
message,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { RateLimitError } from "../core/errors.js";
|
|
2
|
+
|
|
3
|
+
const WINDOW_MS = 5 * 60 * 1000; // 5 minutes
|
|
4
|
+
const MAX_REQUESTS = 30;
|
|
5
|
+
|
|
6
|
+
const hits = new Map();
|
|
7
|
+
|
|
8
|
+
export const rateLimiter = (req, res, next) => {
|
|
9
|
+
// Use IP or UserID as key
|
|
10
|
+
const key =
|
|
11
|
+
req.headers["x-forwarded-for"] || req.socket.remoteAddress || "unknown";
|
|
12
|
+
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
const record = hits.get(key);
|
|
15
|
+
|
|
16
|
+
if (!record) {
|
|
17
|
+
hits.set(key, { count: 1, startTime: now });
|
|
18
|
+
return next();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (now - record.startTime > WINDOW_MS) {
|
|
22
|
+
// Reset window
|
|
23
|
+
record.count = 1;
|
|
24
|
+
record.startTime = now;
|
|
25
|
+
return next();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (record.count >= MAX_REQUESTS) {
|
|
29
|
+
return next(
|
|
30
|
+
new RateLimitError(
|
|
31
|
+
`Rate limit exceeded. Try again in ${Math.ceil((record.startTime + WINDOW_MS - now) / 1000)} seconds.`
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
record.count += 1;
|
|
37
|
+
next();
|
|
38
|
+
};
|