@agentprojectcontext/apx 1.81.0 → 1.89.1
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/package.json +9 -8
- package/skills/apc-context/SKILL.md +14 -7
- package/skills/apx/SKILL.md +62 -2
- package/skills/apx-mcp/SKILL.md +42 -15
- package/src/core/agent/a2a/peers.js +161 -0
- package/src/core/agent/a2a/reply.js +379 -27
- package/src/core/agent/abort.js +25 -0
- package/src/core/agent/agent-model.js +58 -0
- package/src/core/agent/agent-tools.js +101 -0
- package/src/core/agent/author-line.js +110 -0
- package/src/core/agent/build-agent-system.js +52 -18
- package/src/core/agent/channels/voice-context.js +17 -2
- package/src/core/agent/closing-floor.js +117 -0
- package/src/core/agent/constants.js +119 -11
- package/src/core/agent/engine-call.js +56 -0
- package/src/core/agent/group/run-group-turn.js +218 -0
- package/src/core/agent/group/turn-resolver.js +165 -0
- package/src/core/agent/judge.js +138 -11
- package/src/core/agent/loop/side-effects.js +106 -3
- package/src/core/agent/memory.js +31 -18
- package/src/core/agent/model-capabilities.js +47 -0
- package/src/core/agent/model-router.js +11 -40
- package/src/core/agent/prompt-builder.js +47 -0
- package/src/core/agent/prompts/channels/a2a.md +12 -0
- package/src/core/agent/prompts/channels/code.md +3 -1
- package/src/core/agent/prompts/channels/telegram.md +2 -0
- package/src/core/agent/prompts/channels/web_code.md +1 -1
- package/src/core/agent/prompts/channels/whatsapp.md +37 -0
- package/src/core/agent/prompts/core/agent-base.md +2 -0
- package/src/core/agent/prompts/discipline/action.md +1 -0
- package/src/core/agent/prompts/discipline/two-segment.md +2 -1
- package/src/core/agent/prompts/modes/mobility.md +36 -0
- package/src/core/agent/retry.js +42 -0
- package/src/core/agent/run-agent.js +322 -25
- package/src/core/agent/run-turn.js +253 -0
- package/src/core/agent/self-memory.js +18 -52
- package/src/core/agent/skills/agent-skills.js +118 -0
- package/src/core/agent/skills/catalog.js +1 -1
- package/src/core/agent/skills/declared.js +13 -0
- package/src/core/agent/skills/index-store.js +8 -1
- package/src/core/agent/skills/inspector.js +20 -3
- package/src/core/agent/skills/loader.js +18 -3
- package/src/core/agent/skills/media.js +121 -0
- package/src/core/agent/skills/origin.js +61 -0
- package/src/core/agent/skills/sections.js +136 -0
- package/src/core/agent/super-agent.js +54 -14
- package/src/core/agent/tools/handlers/_calendar.js +50 -0
- package/src/core/agent/tools/handlers/add-mcp.js +103 -0
- package/src/core/agent/tools/handlers/asana-create-task.js +3 -1
- package/src/core/agent/tools/handlers/attach-media.js +56 -0
- package/src/core/agent/tools/handlers/calendar-create-event.js +38 -0
- package/src/core/agent/tools/handlers/calendar-find-slot.js +39 -0
- package/src/core/agent/tools/handlers/calendar-list-events.js +39 -0
- package/src/core/agent/tools/handlers/calendar-update-event.js +35 -0
- package/src/core/agent/tools/handlers/call-agent.js +14 -6
- package/src/core/agent/tools/handlers/call-runtime.js +27 -49
- package/src/core/agent/tools/handlers/comment-task.js +78 -0
- package/src/core/agent/tools/handlers/complete-task.js +60 -0
- package/src/core/agent/tools/handlers/configure-agent.js +70 -0
- package/src/core/agent/tools/handlers/create-agent.js +68 -0
- package/src/core/agent/tools/handlers/create-task.js +41 -3
- package/src/core/agent/tools/handlers/import-agent.js +5 -5
- package/src/core/agent/tools/handlers/list-mcp-tools.js +83 -0
- package/src/core/agent/tools/handlers/list-routines.js +46 -0
- package/src/core/agent/tools/handlers/list-tasks.js +8 -1
- package/src/core/agent/tools/handlers/mark-commitment.js +57 -0
- package/src/core/agent/tools/handlers/read-skill.js +64 -0
- package/src/core/agent/tools/handlers/remember-routine.js +2 -8
- package/src/core/agent/tools/handlers/remember.js +82 -6
- package/src/core/agent/tools/handlers/remove-agent.js +42 -0
- package/src/core/agent/tools/handlers/run-routine.js +94 -0
- package/src/core/agent/tools/handlers/run-shell.js +44 -2
- package/src/core/agent/tools/handlers/send-telegram.js +41 -19
- package/src/core/agent/tools/handlers/set-agent-prompt.js +44 -0
- package/src/core/agent/tools/handlers/view-media.js +69 -0
- package/src/core/agent/tools/handlers/write-agent-memory.js +52 -0
- package/src/core/agent/tools/helpers.js +36 -4
- package/src/core/agent/tools/names.js +64 -0
- package/src/core/agent/tools/registry-bridge.js +24 -1
- package/src/core/agent/tools/registry.js +79 -0
- package/src/core/agent/tools/tool-call-parser.js +136 -13
- package/src/core/agent/vision-bridge.js +69 -0
- package/src/core/apc/agent-identity.js +74 -0
- package/src/core/apc/agent-rename-refs.js +187 -0
- package/src/core/apc/agent-write.js +312 -0
- package/src/core/apc/agents-vault.js +1 -1
- package/src/core/apc/blob-keys.js +42 -0
- package/src/core/apc/frontmatter.js +166 -10
- package/src/core/apc/parser.js +33 -83
- package/src/core/apc/paths.js +4 -6
- package/src/core/apc/project-memory.js +44 -0
- package/src/core/apc/projects-helpers.js +33 -0
- package/src/core/apc/scaffold.js +36 -16
- package/src/core/channels/telegram/ask-callbacks.js +103 -0
- package/src/core/channels/telegram/dispatch.js +154 -30
- package/src/core/channels/telegram/helpers.js +59 -7
- package/src/core/channels/telegram/inbound/audio.js +26 -30
- package/src/core/channels/telegram/inbound/file.js +22 -32
- package/src/core/channels/telegram/inbound/photo.js +24 -28
- package/src/core/channels/telegram/progress-gate.js +76 -0
- package/src/core/channels/telegram/reply.js +151 -43
- package/src/core/channels/telegram/voice-note.js +150 -0
- package/src/core/config/index.js +53 -54
- package/src/core/config/paths.js +117 -31
- package/src/core/config/redact.js +89 -4
- package/src/core/config/secret-values.js +2 -4
- package/src/core/constants/actors.js +5 -0
- package/src/core/constants/channels.js +3 -1
- package/src/core/constants/task-categories.js +73 -0
- package/src/core/constants/task-fields.js +28 -0
- package/src/core/engines/_globs.js +33 -0
- package/src/core/engines/anthropic.js +19 -1
- package/src/core/engines/catalog.js +20 -0
- package/src/core/engines/gemini.js +84 -16
- package/src/core/engines/index.js +19 -3
- package/src/core/engines/mock.js +145 -1
- package/src/core/engines/openai-compatible.js +150 -15
- package/src/core/engines/presets.js +36 -9
- package/src/core/engines/zen.js +131 -0
- package/src/core/events/bus.js +140 -0
- package/src/core/events/mascot-notify.js +156 -0
- package/src/core/http-tools/browser.js +366 -36
- package/src/core/http-tools/catalog.js +206 -22
- package/src/core/http-tools/grep.js +62 -7
- package/src/core/http-tools/registry.js +3 -1
- package/src/core/i18n/en.js +34 -3
- package/src/core/i18n/es.js +36 -4
- package/src/core/i18n/index.js +5 -5
- package/src/core/i18n/pt.js +32 -3
- package/src/core/identity/self.js +14 -1
- package/src/core/images/engines/a1111.js +219 -0
- package/src/core/images/engines/index.js +187 -0
- package/src/core/images/engines/mock.js +74 -0
- package/src/core/images/engines/openai.js +130 -0
- package/src/core/images/engines/sdcpp.js +204 -0
- package/src/core/images/engines/shared.js +218 -0
- package/src/core/images/generate.js +260 -0
- package/src/core/integrations/catalog.js +8 -3
- package/src/core/integrations/plugins/_google-oauth.js +179 -0
- package/src/core/integrations/plugins/asana.js +7 -3
- package/src/core/integrations/plugins/calendar.js +404 -0
- package/src/core/integrations/store.js +2 -0
- package/src/core/memory/broker.js +16 -1
- package/src/core/memory/compactor.js +19 -3
- package/src/core/memory/consolidate.js +2 -2
- package/src/core/memory/dated-log.js +66 -0
- package/src/core/memory/embed-engines/custom.js +59 -0
- package/src/core/memory/embed-engines/gemini.js +9 -3
- package/src/core/memory/embed-engines/index.js +87 -15
- package/src/core/memory/embeddings.js +52 -24
- package/src/core/memory/index.js +13 -3
- package/src/core/memory/indexer.js +83 -27
- package/src/core/memory/prune.js +182 -0
- package/src/core/memory/store.js +22 -0
- package/src/core/memory/summarizer.js +72 -8
- package/src/core/mobility/answer.js +80 -0
- package/src/core/mobility/geofence.js +667 -0
- package/src/core/mobility/osm-route.js +282 -0
- package/src/core/mobility/preferences.js +91 -0
- package/src/core/mobility/state.js +245 -0
- package/src/core/mobility/trip-event.js +384 -0
- package/src/core/net/tailscale.js +168 -0
- package/src/core/profiles/bundled/secretary/PROFILE.md +32 -42
- package/src/core/profiles/bundled/secretary/config.schema.es.json +68 -0
- package/src/core/profiles/bundled/secretary/config.schema.json +30 -2
- package/src/core/profiles/bundled/secretary/profile.json +3 -1
- package/src/core/profiles/bundled/secretary/routines/a2a-sweep.json +20 -0
- package/src/core/profiles/bundled/secretary/routines/day-close.json +4 -1
- package/src/core/profiles/bundled/secretary/routines/day-open.json +7 -1
- package/src/core/profiles/bundled/secretary/routines/watch.json +13 -2
- package/src/core/profiles/lifecycle.js +243 -19
- package/src/core/profiles/paths.js +12 -0
- package/src/core/profiles/store.js +47 -0
- package/src/core/routines/active-runs.js +213 -0
- package/src/core/routines/delivery.js +612 -0
- package/src/core/routines/header.js +88 -0
- package/src/core/routines/index.js +26 -0
- package/src/core/routines/run-log.js +78 -0
- package/src/core/routines/runner.js +629 -66
- package/src/core/routines/signals.js +75 -0
- package/src/core/runtime-skills/apc-context/SKILL.md +14 -7
- package/src/core/runtime-skills/apx/SKILL.md +93 -1
- package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
- package/src/core/runtime-skills/apx-agent/SKILL.md +134 -19
- package/src/core/runtime-skills/apx-commitment/SKILL.md +21 -11
- package/src/core/runtime-skills/apx-image/SKILL.md +60 -0
- package/src/core/runtime-skills/apx-integrations/SKILL.md +82 -0
- package/src/core/runtime-skills/apx-mcp/SKILL.md +45 -14
- package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +4 -4
- package/src/core/runtime-skills/apx-profile/SKILL.md +27 -2
- package/src/core/runtime-skills/apx-project/SKILL.md +23 -2
- package/src/core/runtime-skills/apx-routine/SKILL.md +40 -17
- package/src/core/runtime-skills/apx-runtime/SKILL.md +124 -14
- package/src/core/runtime-skills/apx-sessions/SKILL.md +7 -6
- package/src/core/runtime-skills/apx-skill-builder/SKILL.md +15 -13
- package/src/core/runtime-skills/apx-task/SKILL.md +79 -26
- package/src/core/runtime-skills/apx-telegram/SKILL.md +12 -12
- package/src/core/runtime-skills/apx-transcribe/SKILL.md +56 -0
- package/src/core/runtime-skills/apx-voice/SKILL.md +27 -8
- package/src/core/runtime-skills/claude-code/SKILL.md +8 -0
- package/src/core/runtime-skills/openrouter/SKILL.md +8 -2
- package/src/core/runtimes/_spawn.js +9 -1
- package/src/core/runtimes/claude-code.js +11 -2
- package/src/core/runtimes/codex.js +44 -10
- package/src/core/runtimes/index.js +17 -2
- package/src/core/runtimes/opencode.js +88 -9
- package/src/core/runtimes/outcome.js +119 -0
- package/src/core/sessions/index.js +193 -1
- package/src/core/stores/agent-inbox.js +109 -23
- package/src/core/stores/code-sessions.js +21 -0
- package/src/core/stores/commitments.js +27 -3
- package/src/core/stores/conversations.js +220 -20
- package/src/core/stores/deliveries.js +177 -0
- package/src/core/stores/media-archive.js +228 -0
- package/src/core/stores/messages.js +1096 -112
- package/src/core/stores/organization.js +34 -0
- package/src/core/stores/project-memory.js +71 -0
- package/src/core/stores/routine-memory.js +16 -8
- package/src/core/stores/routines.js +27 -49
- package/src/core/stores/sessions.js +4 -8
- package/src/core/stores/tasks.js +212 -18
- package/src/core/stores/turn-record.js +209 -0
- package/src/core/tasks/columns.js +136 -0
- package/src/core/tasks/comment-turn.js +258 -0
- package/src/core/util/time.js +13 -0
- package/src/core/voice/engines/index.js +54 -1
- package/src/core/voice/engines/openai.js +50 -4
- package/src/core/voice/opus.js +67 -0
- package/src/core/voice/pronounceable.js +43 -0
- package/src/core/voice/transcription.js +4 -0
- package/src/core/voice/tts.js +114 -24
- package/src/host/daemon/active-turns.js +188 -0
- package/src/host/daemon/api/admin-config.js +9 -0
- package/src/host/daemon/api/agents.js +99 -90
- package/src/host/daemon/api/artifact-preview.js +7 -6
- package/src/host/daemon/api/artifacts.js +3 -2
- package/src/host/daemon/api/code.js +55 -9
- package/src/host/daemon/api/commitments.js +11 -0
- package/src/host/daemon/api/confirm.js +3 -2
- package/src/host/daemon/api/conversations.js +543 -35
- package/src/host/daemon/api/deck.js +9 -8
- package/src/host/daemon/api/deliveries.js +33 -0
- package/src/host/daemon/api/desktop.js +5 -4
- package/src/host/daemon/api/embeddings.js +15 -6
- package/src/host/daemon/api/engines.js +59 -8
- package/src/host/daemon/api/exec.js +437 -121
- package/src/host/daemon/api/groups.js +215 -0
- package/src/host/daemon/api/images.js +107 -0
- package/src/host/daemon/api/inbox.js +123 -2
- package/src/host/daemon/api/integrations.js +64 -2
- package/src/host/daemon/api/mcps.js +7 -6
- package/src/host/daemon/api/media.js +283 -0
- package/src/host/daemon/api/mobility.js +177 -0
- package/src/host/daemon/api/net.js +182 -0
- package/src/host/daemon/api/pairing.js +38 -5
- package/src/host/daemon/api/profiles.js +30 -2
- package/src/host/daemon/api/routines.js +36 -4
- package/src/host/daemon/api/runtimes.js +74 -39
- package/src/host/daemon/api/sessions-search.js +7 -2
- package/src/host/daemon/api/sessions.js +44 -2
- package/src/host/daemon/api/shared.js +127 -20
- package/src/host/daemon/api/skills.js +11 -4
- package/src/host/daemon/api/super-agent.js +384 -53
- package/src/host/daemon/api/tasks.js +180 -10
- package/src/host/daemon/api/telegram.js +11 -10
- package/src/host/daemon/api/thread-faces.js +159 -0
- package/src/host/daemon/api/top-level.js +3 -3
- package/src/host/daemon/api/transcribe.js +11 -10
- package/src/host/daemon/api/tts.js +97 -4
- package/src/host/daemon/api/turn-abort.js +23 -0
- package/src/host/daemon/api/turns.js +49 -0
- package/src/host/daemon/api/voice.js +5 -4
- package/src/host/daemon/api/web.js +98 -0
- package/src/host/daemon/api.js +39 -0
- package/src/host/daemon/delivery-sweep.js +58 -0
- package/src/host/daemon/desktop-ws.js +5 -23
- package/src/host/daemon/events-ws.js +273 -0
- package/src/host/daemon/index.js +59 -15
- package/src/host/daemon/plugins/desktop/index.js +17 -4
- package/src/host/daemon/plugins/telegram/index.js +78 -40
- package/src/host/daemon/pty-bridge.py +99 -0
- package/src/host/daemon/routines-scheduler.js +1 -0
- package/src/host/daemon/smoke.js +1 -2
- package/src/host/daemon/terminal-ws.js +132 -0
- package/src/host/daemon/tts-keepwarm.js +87 -0
- package/src/host/daemon/wakeup.js +11 -6
- package/src/host/daemon/whisper-server.js +44 -0
- package/src/host/daemon/whisper-server.py +70 -13
- package/src/host/daemon/ws-auth.js +28 -0
- package/src/interfaces/android/app/build.gradle +42 -0
- package/src/interfaces/android/app/proguard-rules.pro +4 -0
- package/src/interfaces/android/app/src/main/AndroidManifest.xml +139 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AlertScreen.java +139 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AndroidAutoDetector.java +106 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxCarAppService.java +57 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxPreferences.java +165 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarAlertStore.java +84 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageActionReceiver.java +47 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageNotification.java +152 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonAddress.java +35 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonClient.java +339 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DeviceLocation.java +38 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ErrandScreen.java +127 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MainActivity.java +872 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationDetector.java +63 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationListenerService.java +453 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsShareIntentParser.java +93 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotBlobCatalog.java +66 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotOverlayService.java +448 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotView.java +249 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MessageFrameParser.java +240 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/NotifyChannels.java +69 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityActionReceiver.java +78 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityNotification.java +161 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/StartupReceiver.java +57 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelEventGate.java +62 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelStatusBanner.java +116 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripLocationService.java +272 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripPlacesSheet.java +203 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripScreen.java +182 -0
- package/src/interfaces/android/app/src/main/res/drawable/app_background.xml +12 -0
- package/src/interfaces/android/app/src/main/res/drawable/ic_apx_app.xml +10 -0
- package/src/interfaces/android/app/src/main/res/drawable/ic_apx_launcher_foreground.xml +12 -0
- package/src/interfaces/android/app/src/main/res/drawable/ic_apx_notification.xml +22 -0
- package/src/interfaces/android/app/src/main/res/drawable/ic_trip_car.xml +37 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/apx_logo.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_amatista.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_aqua.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_brasa.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_campana.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_caracol.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_carbon.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cobalto.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cometa.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_coral.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cubi.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_eclipse.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_faro.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_gajo.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_iris.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_kiwi.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_laguna.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_lila.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_menta.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_nimbo.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_noche.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_onyx.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_orbita.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_ovni.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_papa.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_parche.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_peon.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_perla.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_pino.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_quad.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_rubi.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_saturno.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trebol.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trino.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_volcan.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_zafiro.png +0 -0
- package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
- package/src/interfaces/android/app/src/main/res/raw/apx_notification.mp3 +0 -0
- package/src/interfaces/android/app/src/main/res/values/colors.xml +4 -0
- package/src/interfaces/android/app/src/main/res/values/strings.xml +25 -0
- package/src/interfaces/android/app/src/main/res/values/styles.xml +10 -0
- package/src/interfaces/android/app/src/main/res/values-v31/styles.xml +14 -0
- package/src/interfaces/android/app/src/main/res/xml/automotive_app_desc.xml +9 -0
- package/src/interfaces/android/app/src/main/res/xml/network_security_config.xml +10 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/AndroidAutoDetectorTest.java +85 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/CarAlertStoreTest.java +104 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/DaemonAddressTest.java +23 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsNavigationDetectorTest.java +69 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsShareIntentParserTest.java +31 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MessageFrameParserTest.java +180 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TravelEventGateTest.java +64 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TripPlacesSheetTest.java +66 -0
- package/src/interfaces/android/build.gradle +3 -0
- package/src/interfaces/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/src/interfaces/android/gradle/wrapper/gradle-wrapper.properties +9 -0
- package/src/interfaces/android/gradle.properties +3 -0
- package/src/interfaces/android/gradlew +248 -0
- package/src/interfaces/android/gradlew.bat +82 -0
- package/src/interfaces/android/settings.gradle +18 -0
- package/src/interfaces/cli/commands/a2a.js +145 -4
- package/src/interfaces/cli/commands/agent.js +247 -22
- package/src/interfaces/cli/commands/code.js +1 -1
- package/src/interfaces/cli/commands/commitment.js +5 -0
- package/src/interfaces/cli/commands/config.js +85 -6
- package/src/interfaces/cli/commands/exec.js +90 -1
- package/src/interfaces/cli/commands/image.js +314 -0
- package/src/interfaces/cli/commands/mcp.js +87 -23
- package/src/interfaces/cli/commands/memory.js +35 -22
- package/src/interfaces/cli/commands/panel.js +94 -0
- package/src/interfaces/cli/commands/profile.js +14 -0
- package/src/interfaces/cli/commands/project.js +29 -0
- package/src/interfaces/cli/commands/routine.js +45 -10
- package/src/interfaces/cli/commands/runtime.js +30 -7
- package/src/interfaces/cli/commands/session.js +90 -38
- package/src/interfaces/cli/commands/sessions.js +35 -2
- package/src/interfaces/cli/commands/skills.js +15 -9
- package/src/interfaces/cli/commands/status.js +2 -3
- package/src/interfaces/cli/commands/task.js +54 -5
- package/src/interfaces/cli/commands/telegram.js +12 -4
- package/src/interfaces/cli/commands/transcribe.js +177 -0
- package/src/interfaces/cli/help/index.js +311 -72
- package/src/interfaces/cli/http.js +61 -13
- package/src/interfaces/cli/index.js +1 -1
- package/src/interfaces/cli/routes/agent.js +5 -4
- package/src/interfaces/cli/routes/commitment.js +3 -2
- package/src/interfaces/cli/routes/config.js +7 -2
- package/src/interfaces/cli/routes/image.js +11 -0
- package/src/interfaces/cli/routes/index.js +2 -0
- package/src/interfaces/cli/routes/log.js +1 -1
- package/src/interfaces/cli/routes/memory.js +3 -2
- package/src/interfaces/cli/routes/panel.js +15 -2
- package/src/interfaces/cli/routes/profile.js +3 -2
- package/src/interfaces/cli/routes/transcribe.js +10 -0
- package/src/interfaces/cli/stdin.js +29 -0
- package/src/interfaces/desktop/assets/blobs/amatista.png +0 -0
- package/src/interfaces/desktop/assets/blobs/aqua.png +0 -0
- package/src/interfaces/desktop/assets/blobs/brasa.png +0 -0
- package/src/interfaces/desktop/assets/blobs/campana.png +0 -0
- package/src/interfaces/desktop/assets/blobs/caracol.png +0 -0
- package/src/interfaces/desktop/assets/blobs/carbon.png +0 -0
- package/src/interfaces/desktop/assets/blobs/cobalto.png +0 -0
- package/src/interfaces/desktop/assets/blobs/cometa.png +0 -0
- package/src/interfaces/desktop/assets/blobs/coral.png +0 -0
- package/src/interfaces/desktop/assets/blobs/cubi.png +0 -0
- package/src/interfaces/desktop/assets/blobs/eclipse.png +0 -0
- package/src/interfaces/desktop/assets/blobs/faro.png +0 -0
- package/src/interfaces/desktop/assets/blobs/gajo.png +0 -0
- package/src/interfaces/desktop/assets/blobs/iris.png +0 -0
- package/src/interfaces/desktop/assets/blobs/kiwi.png +0 -0
- package/src/interfaces/desktop/assets/blobs/laguna.png +0 -0
- package/src/interfaces/desktop/assets/blobs/lila.png +0 -0
- package/src/interfaces/desktop/assets/blobs/menta.png +0 -0
- package/src/interfaces/desktop/assets/blobs/nimbo.png +0 -0
- package/src/interfaces/desktop/assets/blobs/noche.png +0 -0
- package/src/interfaces/desktop/assets/blobs/onyx.png +0 -0
- package/src/interfaces/desktop/assets/blobs/orbita.png +0 -0
- package/src/interfaces/desktop/assets/blobs/ovni.png +0 -0
- package/src/interfaces/desktop/assets/blobs/papa.png +0 -0
- package/src/interfaces/desktop/assets/blobs/parche.png +0 -0
- package/src/interfaces/desktop/assets/blobs/peon.png +0 -0
- package/src/interfaces/desktop/assets/blobs/perla.png +0 -0
- package/src/interfaces/desktop/assets/blobs/pino.png +0 -0
- package/src/interfaces/desktop/assets/blobs/quad.png +0 -0
- package/src/interfaces/desktop/assets/blobs/rubi.png +0 -0
- package/src/interfaces/desktop/assets/blobs/saturno.png +0 -0
- package/src/interfaces/desktop/assets/blobs/trebol.png +0 -0
- package/src/interfaces/desktop/assets/blobs/trino.png +0 -0
- package/src/interfaces/desktop/assets/blobs/volcan.png +0 -0
- package/src/interfaces/desktop/assets/blobs/zafiro.png +0 -0
- package/src/interfaces/desktop/assets/notification.mp3 +0 -0
- package/src/interfaces/desktop/main.js +473 -29
- package/src/interfaces/desktop/mascot-blobs.js +41 -0
- package/src/interfaces/desktop/mascot-preload.js +32 -0
- package/src/interfaces/desktop/mascot.css +186 -0
- package/src/interfaces/desktop/mascot.html +40 -0
- package/src/interfaces/desktop/mascot.js +163 -0
- package/src/interfaces/desktop/preload.js +10 -0
- package/src/interfaces/desktop/renderer.js +308 -48
- package/src/interfaces/desktop/style.css +34 -0
- package/src/interfaces/mcp-server/index.js +87 -0
- package/src/interfaces/tui/_shims/util-locale.ts +0 -2
- package/src/interfaces/tui/app.tsx +0 -6
- package/src/interfaces/tui/attach.ts +0 -1
- package/src/interfaces/tui/context/sdk-apx.tsx +0 -4
- package/src/interfaces/tui/plugin/api.tsx +0 -3
- package/src/interfaces/tui/plugin/runtime.ts +0 -3
- package/src/interfaces/tui/thread.ts +0 -16
- package/src/interfaces/web/README.md +22 -14
- package/src/interfaces/web/dist/assets/claude-XtpLmGtF.webp +0 -0
- package/src/interfaces/web/dist/assets/codex-DExPUgvh.webp +0 -0
- package/src/interfaces/web/dist/assets/index-BEoRgIqd.css +32 -0
- package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.br +0 -0
- package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.gz +0 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js +1074 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.br +0 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.gz +0 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map +1 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.br +0 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.gz +0 -0
- package/src/interfaces/web/dist/favicon/dark/site.webmanifest +12 -6
- package/src/interfaces/web/dist/favicon/white/site.webmanifest +12 -6
- package/src/interfaces/web/dist/index.html +22 -6
- package/src/interfaces/web/dist/index.html.br +0 -0
- package/src/interfaces/web/dist/index.html.gz +0 -0
- package/src/interfaces/web/dist/manifest.webmanifest +25 -0
- package/src/interfaces/web/dist/modules/blobs/amatista.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/aqua.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/brasa.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/campana.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/caracol.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/carbon.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/cobalto.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/cometa.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/coral.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/cubi.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/eclipse.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/faro.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/gajo.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/iris.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/kiwi.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/laguna.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/lila.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/menta.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/nimbo.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/noche.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/onyx.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/orbita.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/ovni.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/papa.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/parche.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/peon.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/perla.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/pino.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/quad.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/rubi.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/saturno.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/trebol.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/trino.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/volcan.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/zafiro.png +0 -0
- package/src/interfaces/web/dist/sw.js +106 -0
- package/src/interfaces/web/dist/sw.js.br +0 -0
- package/src/interfaces/web/dist/sw.js.gz +0 -0
- package/src/interfaces/web/eslint.config.js +83 -0
- package/src/interfaces/web/index.html +20 -4
- package/src/interfaces/web/package-lock.json +1651 -1280
- package/src/interfaces/web/package.json +9 -10
- package/src/interfaces/web/pnpm-lock.yaml +867 -869
- package/src/interfaces/web/public/favicon/dark/site.webmanifest +12 -6
- package/src/interfaces/web/public/favicon/white/site.webmanifest +12 -6
- package/src/interfaces/web/public/manifest.webmanifest +25 -0
- package/src/interfaces/web/public/modules/blobs/amatista.png +0 -0
- package/src/interfaces/web/public/modules/blobs/aqua.png +0 -0
- package/src/interfaces/web/public/modules/blobs/brasa.png +0 -0
- package/src/interfaces/web/public/modules/blobs/campana.png +0 -0
- package/src/interfaces/web/public/modules/blobs/caracol.png +0 -0
- package/src/interfaces/web/public/modules/blobs/carbon.png +0 -0
- package/src/interfaces/web/public/modules/blobs/cobalto.png +0 -0
- package/src/interfaces/web/public/modules/blobs/cometa.png +0 -0
- package/src/interfaces/web/public/modules/blobs/coral.png +0 -0
- package/src/interfaces/web/public/modules/blobs/cubi.png +0 -0
- package/src/interfaces/web/public/modules/blobs/eclipse.png +0 -0
- package/src/interfaces/web/public/modules/blobs/faro.png +0 -0
- package/src/interfaces/web/public/modules/blobs/gajo.png +0 -0
- package/src/interfaces/web/public/modules/blobs/iris.png +0 -0
- package/src/interfaces/web/public/modules/blobs/kiwi.png +0 -0
- package/src/interfaces/web/public/modules/blobs/laguna.png +0 -0
- package/src/interfaces/web/public/modules/blobs/lila.png +0 -0
- package/src/interfaces/web/public/modules/blobs/menta.png +0 -0
- package/src/interfaces/web/public/modules/blobs/nimbo.png +0 -0
- package/src/interfaces/web/public/modules/blobs/noche.png +0 -0
- package/src/interfaces/web/public/modules/blobs/onyx.png +0 -0
- package/src/interfaces/web/public/modules/blobs/orbita.png +0 -0
- package/src/interfaces/web/public/modules/blobs/ovni.png +0 -0
- package/src/interfaces/web/public/modules/blobs/papa.png +0 -0
- package/src/interfaces/web/public/modules/blobs/parche.png +0 -0
- package/src/interfaces/web/public/modules/blobs/peon.png +0 -0
- package/src/interfaces/web/public/modules/blobs/perla.png +0 -0
- package/src/interfaces/web/public/modules/blobs/pino.png +0 -0
- package/src/interfaces/web/public/modules/blobs/quad.png +0 -0
- package/src/interfaces/web/public/modules/blobs/rubi.png +0 -0
- package/src/interfaces/web/public/modules/blobs/saturno.png +0 -0
- package/src/interfaces/web/public/modules/blobs/trebol.png +0 -0
- package/src/interfaces/web/public/modules/blobs/trino.png +0 -0
- package/src/interfaces/web/public/modules/blobs/volcan.png +0 -0
- package/src/interfaces/web/public/modules/blobs/zafiro.png +0 -0
- package/src/interfaces/web/public/sw.js +106 -0
- package/src/interfaces/web/src/App.tsx +106 -10
- package/src/interfaces/web/src/assets/cli/claude.webp +0 -0
- package/src/interfaces/web/src/assets/cli/codex.webp +0 -0
- package/src/interfaces/web/src/assets/cli/cursor.svg +1 -0
- package/src/interfaces/web/src/assets/cli/opencode.png +0 -0
- package/src/interfaces/web/src/components/Combobox.tsx +201 -0
- package/src/interfaces/web/src/components/MobileHint.tsx +95 -0
- package/src/interfaces/web/src/components/MobileLinkDialog.tsx +156 -0
- package/src/interfaces/web/src/components/ModelCombobox.tsx +21 -113
- package/src/interfaces/web/src/components/{routines/ReadOnlyBlock.tsx → ReadOnlyBlock.tsx} +13 -4
- package/src/interfaces/web/src/components/Roby.tsx +5 -3
- package/src/interfaces/web/src/components/RobyBubble.tsx +23 -5
- package/src/interfaces/web/src/components/RowMenu.tsx +46 -0
- package/src/interfaces/web/src/components/TimezoneSelect.tsx +4 -1
- package/src/interfaces/web/src/components/Toast.tsx +50 -14
- package/src/interfaces/web/src/components/UiSelect.tsx +12 -5
- package/src/interfaces/web/src/components/agents/AgentAvatar.tsx +171 -0
- package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +42 -11
- package/src/interfaces/web/src/components/agents/AgentModelBadge.tsx +157 -0
- package/src/interfaces/web/src/components/agents/AgentModelSelect.tsx +57 -0
- package/src/interfaces/web/src/components/agents/AgentSkillsPicker.tsx +115 -0
- package/src/interfaces/web/src/components/agents/BlobAvatar.tsx +50 -0
- package/src/interfaces/web/src/components/agents/blobPresets.ts +50 -0
- package/src/interfaces/web/src/components/agents/modelCatalog.ts +66 -0
- package/src/interfaces/web/src/components/chat/ActionGroup.tsx +162 -0
- package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +4 -3
- package/src/interfaces/web/src/components/chat/AskQuestionsCard.tsx +1 -1
- package/src/interfaces/web/src/components/chat/Attachment.tsx +220 -0
- package/src/interfaces/web/src/components/chat/ChatList.tsx +226 -51
- package/src/interfaces/web/src/components/chat/ChatRowActivity.tsx +45 -0
- package/src/interfaces/web/src/components/chat/Composer.tsx +672 -23
- package/src/interfaces/web/src/components/chat/ComposerCommands.tsx +175 -0
- package/src/interfaces/web/src/components/chat/ComposerSuggest.tsx +215 -0
- package/src/interfaces/web/src/components/chat/ContextBar.tsx +133 -65
- package/src/interfaces/web/src/components/chat/InlineAskPanel.tsx +107 -47
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +460 -119
- package/src/interfaces/web/src/components/chat/MessageList.tsx +165 -18
- package/src/interfaces/web/src/components/chat/ReasoningBlock.tsx +58 -0
- package/src/interfaces/web/src/components/chat/SessionPicker.tsx +182 -0
- package/src/interfaces/web/src/components/chat/SkillTrace.tsx +183 -0
- package/src/interfaces/web/src/components/chat/ToolCall.tsx +11 -11
- package/src/interfaces/web/src/components/code/CodeContextTab.tsx +87 -53
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +11 -52
- package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +5 -4
- package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +31 -7
- package/src/interfaces/web/src/components/code/CodeSessionList.tsx +32 -11
- package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +33 -3
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +5 -4
- package/src/interfaces/web/src/components/code/NewCodeSessionDialog.tsx +164 -0
- package/src/interfaces/web/src/components/commitments/CommitmentDetail.tsx +162 -0
- package/src/interfaces/web/src/components/commitments/CommitmentFormDialog.tsx +158 -0
- package/src/interfaces/web/src/components/commitments/CommitmentList.tsx +194 -0
- package/src/interfaces/web/src/components/commitments/commitmentState.tsx +46 -0
- package/src/interfaces/web/src/components/common/BulkActionBar.tsx +60 -0
- package/src/interfaces/web/src/components/common/ConfirmDialog.tsx +4 -1
- package/src/interfaces/web/src/components/common/SelectCheckbox.tsx +40 -0
- package/src/interfaces/web/src/components/common/TabNav.tsx +1 -1
- package/src/interfaces/web/src/components/cron/CronPicker.tsx +1 -1
- package/src/interfaces/web/src/components/files/FileBrowser.tsx +1 -1
- package/src/interfaces/web/src/components/files/FileTree.tsx +6 -4
- package/src/interfaces/web/src/components/files/FileViewer.tsx +9 -5
- package/src/interfaces/web/src/components/files/MarkdownPreview.tsx +45 -9
- package/src/interfaces/web/src/components/images/ImageDefaultsCard.tsx +93 -0
- package/src/interfaces/web/src/components/images/ImageProviderList.tsx +142 -0
- package/src/interfaces/web/src/components/images/ImageProviderModal.tsx +263 -0
- package/src/interfaces/web/src/components/images/ImageTestCard.tsx +142 -0
- package/src/interfaces/web/src/components/inbox/ChannelFilter.tsx +178 -0
- package/src/interfaces/web/src/components/inbox/InboxList.tsx +77 -81
- package/src/interfaces/web/src/components/inbox/InboxRowItem.tsx +140 -0
- package/src/interfaces/web/src/components/integrations/ComingSoonPlugin.tsx +2 -2
- package/src/interfaces/web/src/components/integrations/PluginConnect.tsx +208 -30
- package/src/interfaces/web/src/components/integrations/PluginToolsSection.tsx +1 -1
- package/src/interfaces/web/src/components/layout/ProjectAvatar.tsx +39 -24
- package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +253 -58
- package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +94 -47
- package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +202 -90
- package/src/interfaces/web/src/components/routines/RoutineDetail.tsx +28 -6
- package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +16 -5
- package/src/interfaces/web/src/components/routines/RoutineList.tsx +49 -9
- package/src/interfaces/web/src/components/routines/VarTextarea.tsx +1 -1
- package/src/interfaces/web/src/components/routines/runChat.ts +121 -0
- package/src/interfaces/web/src/components/sessions/SessionDetail.tsx +148 -0
- package/src/interfaces/web/src/components/sessions/SessionTerminal.tsx +98 -0
- package/src/interfaces/web/src/components/settings/AccessPanel.tsx +205 -0
- package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +246 -101
- package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +36 -2
- package/src/interfaces/web/src/components/settings/DevicesPanel.tsx +19 -6
- package/src/interfaces/web/src/components/settings/EmbedProviderList.tsx +274 -0
- package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +26 -6
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +118 -119
- package/src/interfaces/web/src/components/settings/NudgePanel.tsx +4 -3
- package/src/interfaces/web/src/components/settings/PairDeviceDialog.tsx +15 -4
- package/src/interfaces/web/src/components/settings/PanelPrefs.tsx +374 -0
- package/src/interfaces/web/src/components/settings/ProfilePanel.tsx +70 -2
- package/src/interfaces/web/src/components/settings/RoutingPanel.tsx +2 -1
- package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +102 -43
- package/src/interfaces/web/src/components/settings/SkillsManager.tsx +76 -21
- package/src/interfaces/web/src/components/settings/SkillsSettings.tsx +10 -3
- package/src/interfaces/web/src/components/settings/SuperAgentPanel.tsx +8 -0
- package/src/interfaces/web/src/components/settings/TelegramChannelsPanel.tsx +11 -2
- package/src/interfaces/web/src/components/settings/TelegramContactsPanel.tsx +19 -8
- package/src/interfaces/web/src/components/settings/TelegramRolesPanel.tsx +12 -2
- package/src/interfaces/web/src/components/settings/WebPanel.tsx +22 -31
- package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +15 -3
- package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +97 -66
- package/src/interfaces/web/src/components/settings/providers/ProviderTestDialog.tsx +150 -0
- package/src/interfaces/web/src/components/settings/providers/typeStyles.ts +67 -25
- package/src/interfaces/web/src/components/settings/providers/types.ts +3 -0
- package/src/interfaces/web/src/components/tasks/TaskBoard.tsx +201 -0
- package/src/interfaces/web/src/components/tasks/TaskColumnsDialog.tsx +243 -0
- package/src/interfaces/web/src/components/tasks/TaskComments.tsx +167 -0
- package/src/interfaces/web/src/components/tasks/TaskDetail.tsx +335 -0
- package/src/interfaces/web/src/components/tasks/TaskFormDialog.tsx +406 -0
- package/src/interfaces/web/src/components/tasks/TaskList.tsx +258 -0
- package/src/interfaces/web/src/components/tasks/TaskSubtasks.tsx +126 -0
- package/src/interfaces/web/src/components/tasks/columns.ts +51 -0
- package/src/interfaces/web/src/components/tasks/taskStatus.tsx +159 -34
- package/src/interfaces/web/src/components/tasks/taskViewPrefs.ts +73 -0
- package/src/interfaces/web/src/components/tasks/useTaskColumns.ts +31 -0
- package/src/interfaces/web/src/components/ui/chat-input.tsx +136 -12
- package/src/interfaces/web/src/components/ui/context-menu.tsx +15 -198
- package/src/interfaces/web/src/components/ui/dialog.tsx +3 -2
- package/src/interfaces/web/src/components/ui/filter-chips.tsx +4 -0
- package/src/interfaces/web/src/components/ui/popover.tsx +14 -49
- package/src/interfaces/web/src/components/ui/sheet.tsx +2 -1
- package/src/interfaces/web/src/components/ui/spinner.tsx +2 -1
- package/src/interfaces/web/src/components/ui.tsx +62 -23
- package/src/interfaces/web/src/components/voice/VoiceProviderList.tsx +2 -1
- package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +66 -8
- package/src/interfaces/web/src/components/voice/VoiceQvoxInstallCard.tsx +63 -0
- package/src/interfaces/web/src/components/voice/VoiceQvoxStatusCard.tsx +51 -0
- package/src/interfaces/web/src/components/voice/VoiceSttCard.tsx +4 -3
- package/src/interfaces/web/src/components/voice/VoiceTestCard.tsx +38 -4
- package/src/interfaces/web/src/constants/index.ts +1 -0
- package/src/interfaces/web/src/hooks/useChannelPrefs.ts +39 -0
- package/src/interfaces/web/src/hooks/useChat.ts +1092 -85
- package/src/interfaces/web/src/hooks/useChatActivity.ts +24 -0
- package/src/interfaces/web/src/hooks/useInbox.ts +26 -4
- package/src/interfaces/web/src/hooks/useLiveMessages.ts +16 -0
- package/src/interfaces/web/src/hooks/useOllamaModels.ts +79 -0
- package/src/interfaces/web/src/hooks/useRecorder.ts +198 -0
- package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +23 -6
- package/src/interfaces/web/src/i18n/en.ts +832 -175
- package/src/interfaces/web/src/i18n/es.ts +841 -188
- package/src/interfaces/web/src/lib/api/agents.ts +23 -4
- package/src/interfaces/web/src/lib/api/code.ts +17 -0
- package/src/interfaces/web/src/lib/api/commitments.ts +7 -1
- package/src/interfaces/web/src/lib/api/conversations.ts +38 -11
- package/src/interfaces/web/src/lib/api/embeddings.ts +3 -0
- package/src/interfaces/web/src/lib/api/engines.ts +14 -0
- package/src/interfaces/web/src/lib/api/groups.ts +93 -0
- package/src/interfaces/web/src/lib/api/images.ts +193 -0
- package/src/interfaces/web/src/lib/api/inbox.ts +29 -5
- package/src/interfaces/web/src/lib/api/integrations.ts +9 -0
- package/src/interfaces/web/src/lib/api/media.ts +126 -0
- package/src/interfaces/web/src/lib/api/net.ts +44 -0
- package/src/interfaces/web/src/lib/api/profiles.ts +12 -0
- package/src/interfaces/web/src/lib/api/projects.ts +9 -1
- package/src/interfaces/web/src/lib/api/routines.ts +9 -1
- package/src/interfaces/web/src/lib/api/sessions.ts +18 -0
- package/src/interfaces/web/src/lib/api/skills.ts +8 -0
- package/src/interfaces/web/src/lib/api/super_agent.ts +3 -0
- package/src/interfaces/web/src/lib/api/tasks.ts +46 -6
- package/src/interfaces/web/src/lib/api/tools.ts +2 -0
- package/src/interfaces/web/src/lib/api/transcribe.ts +31 -0
- package/src/interfaces/web/src/lib/api/turns.ts +20 -0
- package/src/interfaces/web/src/lib/api/voice.ts +72 -2
- package/src/interfaces/web/src/lib/api.ts +3 -0
- package/src/interfaces/web/src/lib/channels.ts +214 -0
- package/src/interfaces/web/src/lib/chat-activity.ts +175 -0
- package/src/interfaces/web/src/lib/chat-prefs.ts +48 -0
- package/src/interfaces/web/src/lib/code-context.ts +0 -2
- package/src/interfaces/web/src/lib/http.ts +51 -17
- package/src/interfaces/web/src/lib/inbox-selection.ts +29 -0
- package/src/interfaces/web/src/lib/live.ts +252 -0
- package/src/interfaces/web/src/lib/net.ts +211 -0
- package/src/interfaces/web/src/lib/notify.ts +331 -0
- package/src/interfaces/web/src/lib/projectNav.ts +40 -0
- package/src/interfaces/web/src/lib/pwa.ts +115 -0
- package/src/interfaces/web/src/lib/qvox.ts +24 -0
- package/src/interfaces/web/src/lib/slug.ts +3 -0
- package/src/interfaces/web/src/lib/tone.ts +150 -0
- package/src/interfaces/web/src/main.tsx +15 -4
- package/src/interfaces/web/src/screens/ApxAdminScreen.tsx +33 -11
- package/src/interfaces/web/src/screens/InboxScreen.tsx +126 -71
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -4
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +7 -4
- package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +30 -9
- package/src/interfaces/web/src/screens/base/CommitmentsTab.tsx +150 -168
- package/src/interfaces/web/src/screens/base/LogsTab.tsx +8 -7
- package/src/interfaces/web/src/screens/base/SessionsTab.tsx +99 -37
- package/src/interfaces/web/src/screens/base/WorkspacesTab.tsx +1 -1
- package/src/interfaces/web/src/screens/mobile/MobileChat.tsx +85 -0
- package/src/interfaces/web/src/screens/mobile/MobileChatList.tsx +244 -0
- package/src/interfaces/web/src/screens/mobile/MobileCommitments.tsx +350 -0
- package/src/interfaces/web/src/screens/mobile/MobileScreen.tsx +138 -0
- package/src/interfaces/web/src/screens/mobile/MobileTabBar.tsx +105 -0
- package/src/interfaces/web/src/screens/mobile/MobileTasks.tsx +407 -0
- package/src/interfaces/web/src/screens/mobile/NewChatSheet.tsx +313 -0
- package/src/interfaces/web/src/screens/mobile/mobileList.tsx +194 -0
- package/src/interfaces/web/src/screens/mobile/routes.ts +239 -0
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +135 -64
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +0 -1
- package/src/interfaces/web/src/screens/modules/ImagesScreen.tsx +172 -0
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +36 -4
- package/src/interfaces/web/src/screens/project/AgentBrainGraph.tsx +117 -30
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +537 -92
- package/src/interfaces/web/src/screens/project/AgentsTab.tsx +344 -93
- package/src/interfaces/web/src/screens/project/ArtifactsTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +1160 -94
- package/src/interfaces/web/src/screens/project/IntegrationsTab.tsx +3 -2
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +62 -14
- package/src/interfaces/web/src/screens/project/Overview.tsx +36 -15
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +93 -8
- package/src/interfaces/web/src/screens/project/SkillsTab.tsx +4 -1
- package/src/interfaces/web/src/screens/project/StructureTab.tsx +5 -4
- package/src/interfaces/web/src/screens/project/TasksTab.tsx +351 -129
- package/src/interfaces/web/src/screens/project/VarsTab.tsx +17 -5
- package/src/interfaces/web/src/styles.css +106 -31
- package/src/interfaces/web/src/types/daemon.ts +246 -2
- package/src/interfaces/web/src/vite-env.d.ts +20 -0
- package/src/interfaces/tui/plugin/command-shim.ts +0 -109
- package/src/interfaces/web/components.json +0 -25
- package/src/interfaces/web/dist/assets/index-CvEoGtTf.js +0 -849
- package/src/interfaces/web/dist/assets/index-CvEoGtTf.js.map +0 -1
- package/src/interfaces/web/dist/assets/index-DzBBXFaO.css +0 -1
- package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +0 -142
- package/src/interfaces/web/src/components/ui/accordion.tsx +0 -72
- package/src/interfaces/web/src/components/ui/alert-dialog.tsx +0 -187
- package/src/interfaces/web/src/components/ui/alert.tsx +0 -76
- package/src/interfaces/web/src/components/ui/aspect-ratio.tsx +0 -22
- package/src/interfaces/web/src/components/ui/avatar.tsx +0 -107
- package/src/interfaces/web/src/components/ui/breadcrumb.tsx +0 -125
- package/src/interfaces/web/src/components/ui/button-group.tsx +0 -87
- package/src/interfaces/web/src/components/ui/calendar.tsx +0 -221
- package/src/interfaces/web/src/components/ui/card.tsx +0 -103
- package/src/interfaces/web/src/components/ui/carousel.tsx +0 -242
- package/src/interfaces/web/src/components/ui/chart.tsx +0 -371
- package/src/interfaces/web/src/components/ui/checkbox.tsx +0 -29
- package/src/interfaces/web/src/components/ui/collapsible.tsx +0 -19
- package/src/interfaces/web/src/components/ui/combobox.tsx +0 -295
- package/src/interfaces/web/src/components/ui/command.tsx +0 -196
- package/src/interfaces/web/src/components/ui/direction.tsx +0 -4
- package/src/interfaces/web/src/components/ui/drawer.tsx +0 -134
- package/src/interfaces/web/src/components/ui/empty.tsx +0 -104
- package/src/interfaces/web/src/components/ui/field.tsx +0 -236
- package/src/interfaces/web/src/components/ui/hover-card.tsx +0 -51
- package/src/interfaces/web/src/components/ui/input-otp.tsx +0 -85
- package/src/interfaces/web/src/components/ui/item.tsx +0 -201
- package/src/interfaces/web/src/components/ui/menubar.tsx +0 -280
- package/src/interfaces/web/src/components/ui/native-select.tsx +0 -61
- package/src/interfaces/web/src/components/ui/navigation-menu.tsx +0 -168
- package/src/interfaces/web/src/components/ui/pagination.tsx +0 -130
- package/src/interfaces/web/src/components/ui/progress.tsx +0 -83
- package/src/interfaces/web/src/components/ui/radio-group.tsx +0 -36
- package/src/interfaces/web/src/components/ui/resizable.tsx +0 -50
- package/src/interfaces/web/src/components/ui/scroll-area.tsx +0 -53
- package/src/interfaces/web/src/components/ui/sidebar.tsx +0 -726
- package/src/interfaces/web/src/components/ui/slider.tsx +0 -52
- package/src/interfaces/web/src/components/ui/sonner.tsx +0 -49
- package/src/interfaces/web/src/components/ui/table.tsx +0 -116
- package/src/interfaces/web/src/components/ui/toggle-group.tsx +0 -87
- package/src/interfaces/web/src/components/ui/toggle.tsx +0 -45
- package/src/interfaces/web/src/hooks/use-mobile.ts +0 -19
- package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +0 -161
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { listTasksAcrossProjects } from "#core/stores/tasks.js";
|
|
2
|
+
|
|
3
|
+
const USER_AGENT = "APX mobility assistant/1.0";
|
|
4
|
+
const RADIUS_METERS = 700;
|
|
5
|
+
|
|
6
|
+
const NEEDS = [
|
|
7
|
+
{
|
|
8
|
+
id: "stationery",
|
|
9
|
+
query: "papelería",
|
|
10
|
+
match: /cartulina|papeler[ií]a|cuaderno|l[aá]piz|lapicera|fibra|marcador|impresi[oó]n/i,
|
|
11
|
+
selectors: ['["shop"~"stationery|books|convenience|kiosk"]'],
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: "supermarket",
|
|
15
|
+
query: "supermercado",
|
|
16
|
+
// "en el súper" is what anyone actually writes, and it matched nothing —
|
|
17
|
+
// the vocabulary only knew the full word. Article-anchored on purpose:
|
|
18
|
+
// a bare /súper/ would fire on "súper importante".
|
|
19
|
+
match: /an[oó]nima|supermercado|mercado|(?:al|el|del) s[uú]per\b|hacer (?:las )?compras|comprar comida|almac[eé]n|\bpan\b|pan lactal/i,
|
|
20
|
+
selectors: ['["shop"~"supermarket|convenience"]'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "bakery",
|
|
24
|
+
query: "panadería",
|
|
25
|
+
// Bread is the clearest case of an errand with no single answer: the
|
|
26
|
+
// bakery on the corner and both supermarkets all satisfy it, which is why
|
|
27
|
+
// it deliberately overlaps the supermarket need above. The trip plan keeps
|
|
28
|
+
// every candidate and settles on one — see core/mobility/geofence.js.
|
|
29
|
+
// "pan lactal" is the exception: it is a packaged supermarket product and
|
|
30
|
+
// a bakery does not carry it, so a bare /\bpan\b/ sent the owner to
|
|
31
|
+
// panaderías for an errand only a súper can satisfy. Everything else about
|
|
32
|
+
// bread still overlaps the supermarket need on purpose.
|
|
33
|
+
match: /\bpan\b(?! *lactal)|panader[ií]a|factura[s]?\b|medialunas?/i,
|
|
34
|
+
selectors: ['["shop"~"bakery|pastry"]'],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "pharmacy",
|
|
38
|
+
query: "farmacia",
|
|
39
|
+
match: /farmacia|medicamento|remedio|receta/i,
|
|
40
|
+
selectors: ['["amenity"="pharmacy"]'],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "hardware",
|
|
44
|
+
query: "ferretería",
|
|
45
|
+
match: /ferreter[ií]a|tornillo|herramienta|pintura/i,
|
|
46
|
+
selectors: ['["shop"~"hardware|doityourself|paint"]'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "fuel",
|
|
50
|
+
query: "estación de servicio",
|
|
51
|
+
match: /nafta|combustible|cargar tanque|estaci[oó]n de servicio/i,
|
|
52
|
+
selectors: ['["amenity"="fuel"]'],
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export function activeTasks(projects) {
|
|
57
|
+
const entries = [];
|
|
58
|
+
for (const entry of projects?.list?.() || []) {
|
|
59
|
+
let project;
|
|
60
|
+
try { project = projects.get(entry.id); } catch { continue; }
|
|
61
|
+
if (!project?.storagePath) continue;
|
|
62
|
+
entries.push({ ...entry, storagePath: project.storagePath });
|
|
63
|
+
}
|
|
64
|
+
const { tasks } = listTasksAcrossProjects(entries, { limit: 100 });
|
|
65
|
+
return tasks.filter((task) => !["done", "dropped", "cancelled"].includes(task.status));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function taskNeeds(task) {
|
|
69
|
+
const text = `${task.title || ""} ${task.body || ""} ${(task.tags || []).join(" ")}`;
|
|
70
|
+
return NEEDS.filter((need) => need.match.test(text)).map((need) => ({
|
|
71
|
+
...need,
|
|
72
|
+
query: need.id === "supermarket" && /an[oó]nima/i.test(text) ? "La Anónima" : need.query,
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function getJson(url, options = {}, fetchFn = fetch) {
|
|
77
|
+
const response = await fetchFn(url, {
|
|
78
|
+
...options,
|
|
79
|
+
headers: { "user-agent": USER_AGENT, accept: "application/json", ...(options.headers || {}) },
|
|
80
|
+
signal: options.signal || AbortSignal.timeout(8_000),
|
|
81
|
+
});
|
|
82
|
+
if (!response.ok) throw new Error(`map service HTTP ${response.status}`);
|
|
83
|
+
return response.json();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function geocodeDestination(query, origin, fetchFn = fetch) {
|
|
87
|
+
if (!query) return null;
|
|
88
|
+
const params = new URLSearchParams({ format: "jsonv2", limit: "1", countrycodes: "ar", q: query });
|
|
89
|
+
if (origin) {
|
|
90
|
+
const delta = 1.5;
|
|
91
|
+
params.set("viewbox", `${origin.longitude - delta},${origin.latitude + delta},${origin.longitude + delta},${origin.latitude - delta}`);
|
|
92
|
+
params.set("bounded", "0");
|
|
93
|
+
}
|
|
94
|
+
const rows = await getJson(`https://nominatim.openstreetmap.org/search?${params}`, {}, fetchFn);
|
|
95
|
+
if (!rows?.length) return null;
|
|
96
|
+
return { latitude: Number(rows[0].lat), longitude: Number(rows[0].lon), label: rows[0].display_name || query };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function drivingRoute(origin, destination, fetchFn = fetch) {
|
|
100
|
+
try {
|
|
101
|
+
const request = {
|
|
102
|
+
locations: [
|
|
103
|
+
{ lat: origin.latitude, lon: origin.longitude },
|
|
104
|
+
{ lat: destination.latitude, lon: destination.longitude },
|
|
105
|
+
],
|
|
106
|
+
costing: "auto",
|
|
107
|
+
directions_options: { units: "kilometers" },
|
|
108
|
+
};
|
|
109
|
+
const data = await getJson(`https://valhalla1.openstreetmap.de/route?json=${encodeURIComponent(JSON.stringify(request))}`, {}, fetchFn);
|
|
110
|
+
const summary = data?.trip?.summary;
|
|
111
|
+
const shape = data?.trip?.legs?.[0]?.shape;
|
|
112
|
+
if (summary && shape) {
|
|
113
|
+
return {
|
|
114
|
+
distance_m: Number(summary.length) * 1000,
|
|
115
|
+
duration_s: Number(summary.time),
|
|
116
|
+
points: decodePolyline(shape),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
} catch { /* fall through to public OSRM endpoints */ }
|
|
120
|
+
const coords = `${origin.longitude},${origin.latitude};${destination.longitude},${destination.latitude}`;
|
|
121
|
+
const bases = [
|
|
122
|
+
"https://routing.openstreetmap.de/routed-car/route/v1/driving",
|
|
123
|
+
"https://router.project-osrm.org/route/v1/driving",
|
|
124
|
+
];
|
|
125
|
+
for (const base of bases) {
|
|
126
|
+
try {
|
|
127
|
+
const data = await getJson(`${base}/${coords}?overview=full&geometries=geojson`, {}, fetchFn);
|
|
128
|
+
const route = data?.routes?.[0];
|
|
129
|
+
if (!route?.geometry?.coordinates?.length) continue;
|
|
130
|
+
return {
|
|
131
|
+
distance_m: route.distance,
|
|
132
|
+
duration_s: route.duration,
|
|
133
|
+
points: route.geometry.coordinates.map(([longitude, latitude]) => ({ latitude, longitude })),
|
|
134
|
+
};
|
|
135
|
+
} catch { /* try the next public OSRM endpoint */ }
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function decodePolyline(encoded, precision = 6) {
|
|
141
|
+
const factor = 10 ** precision;
|
|
142
|
+
const points = [];
|
|
143
|
+
let index = 0;
|
|
144
|
+
let latitude = 0;
|
|
145
|
+
let longitude = 0;
|
|
146
|
+
while (index < encoded.length) {
|
|
147
|
+
const read = () => {
|
|
148
|
+
let result = 0;
|
|
149
|
+
let shift = 0;
|
|
150
|
+
let byte;
|
|
151
|
+
do {
|
|
152
|
+
byte = encoded.charCodeAt(index++) - 63;
|
|
153
|
+
result |= (byte & 0x1f) << shift;
|
|
154
|
+
shift += 5;
|
|
155
|
+
} while (byte >= 0x20 && index <= encoded.length);
|
|
156
|
+
return (result & 1) ? ~(result >> 1) : result >> 1;
|
|
157
|
+
};
|
|
158
|
+
latitude += read();
|
|
159
|
+
longitude += read();
|
|
160
|
+
points.push({ latitude: latitude / factor, longitude: longitude / factor });
|
|
161
|
+
}
|
|
162
|
+
return points;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Nominatim `viewbox` string (left,top,right,bottom) around a set of points.
|
|
167
|
+
*
|
|
168
|
+
* `padDegrees` exists because the live geofence (./geofence.js) searches around
|
|
169
|
+
* ONE point — the phone's current position — and a box built from a single
|
|
170
|
+
* point has zero area, which Nominatim answers with nothing at all. A route has
|
|
171
|
+
* its own extent and needs no padding.
|
|
172
|
+
*/
|
|
173
|
+
export function boundingBox(points, padDegrees = 0) {
|
|
174
|
+
const latitudes = points.map((point) => point.latitude);
|
|
175
|
+
const longitudes = points.map((point) => point.longitude);
|
|
176
|
+
const left = Math.min(...longitudes) - padDegrees;
|
|
177
|
+
const right = Math.max(...longitudes) + padDegrees;
|
|
178
|
+
const top = Math.max(...latitudes) + padDegrees;
|
|
179
|
+
const bottom = Math.min(...latitudes) - padDegrees;
|
|
180
|
+
return `${left},${top},${right},${bottom}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function nearbyPois(route, needs, fetchFn = fetch, { padDegrees = 0 } = {}) {
|
|
184
|
+
if (!route?.points?.length || !needs.length) return [];
|
|
185
|
+
const viewbox = boundingBox(route.points, padDegrees);
|
|
186
|
+
const seen = new Set();
|
|
187
|
+
const rows = [];
|
|
188
|
+
for (const [index, need] of needs.slice(0, 3).entries()) {
|
|
189
|
+
if (index > 0 && fetchFn === fetch) await new Promise((resolve) => setTimeout(resolve, 1_100));
|
|
190
|
+
const params = new URLSearchParams({
|
|
191
|
+
format: "jsonv2", limit: "15", countrycodes: "ar", bounded: "1", viewbox, q: need.query,
|
|
192
|
+
});
|
|
193
|
+
const data = await getJson(`https://nominatim.openstreetmap.org/search?${params}`, {}, fetchFn);
|
|
194
|
+
for (const element of data || []) {
|
|
195
|
+
const latitude = Number(element.lat);
|
|
196
|
+
const longitude = Number(element.lon);
|
|
197
|
+
if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) continue;
|
|
198
|
+
const key = `${need.id}:${latitude.toFixed(5)},${longitude.toFixed(5)}`;
|
|
199
|
+
if (seen.has(key)) continue;
|
|
200
|
+
seen.add(key);
|
|
201
|
+
// Nominatim's display_name is "<name>, <street>, <number>, <city>, …".
|
|
202
|
+
// The first segment is the name; the REST is the street address, and it
|
|
203
|
+
// used to be discarded. At the wheel "Farmacia del Puente" is not enough
|
|
204
|
+
// to find a door — the address is what tells you which corner it is on,
|
|
205
|
+
// and it is the one thing a spoken alert has to say.
|
|
206
|
+
const parts = String(element.display_name || need.query).split(",");
|
|
207
|
+
rows.push({
|
|
208
|
+
name: parts[0].trim(),
|
|
209
|
+
address: parts.slice(1).join(",").trim() || null,
|
|
210
|
+
latitude,
|
|
211
|
+
longitude,
|
|
212
|
+
tags: { need_id: need.id },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return rows;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function haversineMeters(a, b) {
|
|
220
|
+
const radians = (value) => value * Math.PI / 180;
|
|
221
|
+
const dLat = radians(b.latitude - a.latitude);
|
|
222
|
+
const dLon = radians(b.longitude - a.longitude);
|
|
223
|
+
const lat1 = radians(a.latitude);
|
|
224
|
+
const lat2 = radians(b.latitude);
|
|
225
|
+
const h = Math.sin(dLat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2;
|
|
226
|
+
return 6_371_000 * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function distanceToRoute(poi, route) {
|
|
230
|
+
return Math.min(...route.points.map((point) => haversineMeters(poi, point)));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function mapLink(poi) {
|
|
234
|
+
return `https://www.google.com/maps/search/?api=1&query=${poi.latitude},${poi.longitude}`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function needMatchesPoi(need, poi) {
|
|
238
|
+
if (poi.tags.need_id) return poi.tags.need_id === need.id;
|
|
239
|
+
const shop = poi.tags.shop || "";
|
|
240
|
+
const amenity = poi.tags.amenity || "";
|
|
241
|
+
if (need.id === "stationery") return /stationery|books|convenience|kiosk/.test(shop);
|
|
242
|
+
if (need.id === "supermarket") return /supermarket|convenience/.test(shop);
|
|
243
|
+
if (need.id === "pharmacy") return amenity === "pharmacy";
|
|
244
|
+
if (need.id === "hardware") return /hardware|doityourself|paint/.test(shop);
|
|
245
|
+
if (need.id === "fuel") return amenity === "fuel";
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export async function enrichMobilityEvent(event, ctx, fetchFn = fetch) {
|
|
250
|
+
if (!event.origin || !event.destination) return { checked: false, reason: "missing-endpoint", candidates: [] };
|
|
251
|
+
const tasks = activeTasks(ctx.projects).map((task) => ({ task, needs: taskNeeds(task) })).filter((row) => row.needs.length);
|
|
252
|
+
if (!tasks.length) return { checked: true, reason: "no-physical-needs", candidates: [] };
|
|
253
|
+
try {
|
|
254
|
+
const destination = await geocodeDestination(event.destination, event.origin, fetchFn);
|
|
255
|
+
if (!destination) return { checked: false, reason: "destination-not-geocoded", candidates: [] };
|
|
256
|
+
const route = await drivingRoute(event.origin, destination, fetchFn);
|
|
257
|
+
if (!route) return { checked: false, reason: "route-not-found", candidates: [] };
|
|
258
|
+
const needs = [...new Map(tasks.flatMap((row) => row.needs).map((need) => [`${need.id}:${need.query}`, need])).values()];
|
|
259
|
+
const pois = await nearbyPois(route, needs, fetchFn);
|
|
260
|
+
const candidates = [];
|
|
261
|
+
for (const row of tasks) {
|
|
262
|
+
const compatible = pois.filter((poi) => row.needs.some((need) => needMatchesPoi(need, poi)));
|
|
263
|
+
for (const poi of compatible) {
|
|
264
|
+
const distance_m = Math.round(distanceToRoute(poi, route));
|
|
265
|
+
if (distance_m > RADIUS_METERS) continue;
|
|
266
|
+
candidates.push({ task_id: row.task.id, task: row.task.title, place: poi.name, distance_m, maps_url: mapLink(poi) });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
candidates.sort((a, b) => a.distance_m - b.distance_m);
|
|
270
|
+
const unique = candidates.filter((candidate, index, all) => all.findIndex((other) => other.task_id === candidate.task_id && other.place === candidate.place) === index);
|
|
271
|
+
return {
|
|
272
|
+
checked: true,
|
|
273
|
+
origin: event.origin,
|
|
274
|
+
destination,
|
|
275
|
+
route: { distance_m: route.distance_m, duration_s: route.duration_s },
|
|
276
|
+
radius_m: RADIUS_METERS,
|
|
277
|
+
candidates: unique.slice(0, 3),
|
|
278
|
+
};
|
|
279
|
+
} catch (error) {
|
|
280
|
+
return { checked: false, reason: error?.message || String(error), candidates: [] };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { MOBILITY_PATH } from "#core/config/paths.js";
|
|
4
|
+
|
|
5
|
+
function dayKey(date = new Date()) {
|
|
6
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function read() {
|
|
10
|
+
try {
|
|
11
|
+
const value = JSON.parse(fs.readFileSync(MOBILITY_PATH, "utf8"));
|
|
12
|
+
return value && typeof value === "object" ? value : {};
|
|
13
|
+
} catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function write(value) {
|
|
19
|
+
fs.mkdirSync(path.dirname(MOBILITY_PATH), { recursive: true });
|
|
20
|
+
fs.writeFileSync(MOBILITY_PATH, JSON.stringify(value, null, 2));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function silenceMobilityToday(now = new Date()) {
|
|
24
|
+
const state = read();
|
|
25
|
+
state.silent_day = dayKey(now);
|
|
26
|
+
write(state);
|
|
27
|
+
return state.silent_day;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isMobilitySilentToday(now = new Date()) {
|
|
31
|
+
return read().silent_day === dayKey(now);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isMobilitySilenceCommand(text = "") {
|
|
35
|
+
const normalized = String(text).toLowerCase().normalize("NFD").replace(/\p{M}+/gu, "");
|
|
36
|
+
return /(?:no me (?:comentes|avises|molestes)|no (?:comentes|avises)|silencio).*(?:por )?hoy/.test(normalized);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function _resetMobilityPreferencesForTest() {
|
|
40
|
+
try { fs.rmSync(MOBILITY_PATH, { force: true }); } catch { /* nothing to remove */ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── where a mobility reminder is delivered ──────────────────────────────────
|
|
44
|
+
//
|
|
45
|
+
// It used to be "Telegram, always", because Telegram was the only surface that
|
|
46
|
+
// could draw a card with buttons. That is no longer true: the alert is its own
|
|
47
|
+
// frame on the events socket and the phone renders it natively, so the Telegram
|
|
48
|
+
// copy became a second notification for the same event — which is how a chat
|
|
49
|
+
// used for everything else fills up with driving reminders.
|
|
50
|
+
//
|
|
51
|
+
// APP IS THE DEFAULT. The reminder is for someone at the wheel, and the surface
|
|
52
|
+
// they are looking at is the phone in the cradle or the head unit, not a chat.
|
|
53
|
+
|
|
54
|
+
/** Every delivery surface a mobility reminder can use. */
|
|
55
|
+
export const MOBILITY_SURFACES = Object.freeze(["app", "telegram"]);
|
|
56
|
+
|
|
57
|
+
/** What ships when nothing is configured. */
|
|
58
|
+
export const DEFAULT_MOBILITY_SURFACES = Object.freeze(["app"]);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Which surfaces this install delivers mobility reminders on.
|
|
62
|
+
*
|
|
63
|
+
* Accepts what a person would plausibly write in config.json or type at the
|
|
64
|
+
* CLI: a list, a single string, "both", or "none" to turn reminders off
|
|
65
|
+
* without unpairing anything.
|
|
66
|
+
*
|
|
67
|
+
* config.mobility.notify = "app" | "telegram" | "both" | "none" | [..]
|
|
68
|
+
*
|
|
69
|
+
* Anything unrecognised falls back to the default rather than delivering
|
|
70
|
+
* nowhere — a typo in a config file must not silently disable the alerts.
|
|
71
|
+
*
|
|
72
|
+
* @returns {{app: boolean, telegram: boolean}}
|
|
73
|
+
*/
|
|
74
|
+
export function mobilitySurfaces(globalConfig) {
|
|
75
|
+
const raw = globalConfig?.mobility?.notify;
|
|
76
|
+
if (raw === "none" || (Array.isArray(raw) && raw.length === 0)) {
|
|
77
|
+
return { app: false, telegram: false };
|
|
78
|
+
}
|
|
79
|
+
const list = raw === "both"
|
|
80
|
+
? MOBILITY_SURFACES
|
|
81
|
+
: Array.isArray(raw)
|
|
82
|
+
? raw
|
|
83
|
+
: typeof raw === "string" && raw.trim()
|
|
84
|
+
? [raw]
|
|
85
|
+
: DEFAULT_MOBILITY_SURFACES;
|
|
86
|
+
const wanted = list
|
|
87
|
+
.map((s) => String(s || "").trim().toLowerCase())
|
|
88
|
+
.filter((s) => MOBILITY_SURFACES.includes(s));
|
|
89
|
+
const chosen = wanted.length ? wanted : DEFAULT_MOBILITY_SURFACES;
|
|
90
|
+
return { app: chosen.includes("app"), telegram: chosen.includes("telegram") };
|
|
91
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { MOBILITY_PATH } from "#core/config/paths.js";
|
|
4
|
+
|
|
5
|
+
const RECENT_QUESTION_MS = 20 * 60_000;
|
|
6
|
+
|
|
7
|
+
let current = null;
|
|
8
|
+
let lastQuestion = null;
|
|
9
|
+
let lastResponse = null;
|
|
10
|
+
let alerts = [];
|
|
11
|
+
let loadedPath = null;
|
|
12
|
+
|
|
13
|
+
// A proximity alert outlives the trip that produced it: the follow-up ("did you
|
|
14
|
+
// actually stop there?") is asked once the driving is over. So the log is
|
|
15
|
+
// persisted, not just held in memory — a daemon restart mid-trip must not
|
|
16
|
+
// re-announce a place the owner was already told about. Bounded, because this
|
|
17
|
+
// is a reminder log and not a location history.
|
|
18
|
+
const MAX_ALERTS = 50;
|
|
19
|
+
|
|
20
|
+
function clean(value, max = 900) {
|
|
21
|
+
return typeof value === "string" ? value.trim().slice(0, max) : "";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readFile() {
|
|
25
|
+
try {
|
|
26
|
+
const value = JSON.parse(fs.readFileSync(MOBILITY_PATH, "utf8"));
|
|
27
|
+
return value && typeof value === "object" ? value : {};
|
|
28
|
+
} catch {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function ensureLoaded() {
|
|
34
|
+
if (loadedPath === MOBILITY_PATH) return;
|
|
35
|
+
const file = readFile();
|
|
36
|
+
const saved = file.mobility_context || {};
|
|
37
|
+
current = saved.trip || null;
|
|
38
|
+
lastQuestion = saved.last_question || null;
|
|
39
|
+
lastResponse = saved.last_response || null;
|
|
40
|
+
alerts = Array.isArray(file.mobility_alerts) ? file.mobility_alerts : [];
|
|
41
|
+
loadedPath = MOBILITY_PATH;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function persist() {
|
|
45
|
+
const state = readFile();
|
|
46
|
+
state.mobility_context = {
|
|
47
|
+
trip: current,
|
|
48
|
+
last_question: lastQuestion,
|
|
49
|
+
last_response: lastResponse,
|
|
50
|
+
};
|
|
51
|
+
state.mobility_alerts = alerts.slice(-MAX_ALERTS);
|
|
52
|
+
fs.mkdirSync(path.dirname(MOBILITY_PATH), { recursive: true });
|
|
53
|
+
fs.writeFileSync(MOBILITY_PATH, JSON.stringify(state, null, 2));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function observeMobilityEvent(event, now = Date.now()) {
|
|
57
|
+
ensureLoaded();
|
|
58
|
+
if (!event || event.duplicate) return;
|
|
59
|
+
if (event.type === "trip.ended") {
|
|
60
|
+
if (!current || !event.trip_id || current.trip_id === event.trip_id) {
|
|
61
|
+
current = current ? { ...current, active: false, ended_at: event.occurred_at } : null;
|
|
62
|
+
}
|
|
63
|
+
persist();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (event.type !== "trip.started") return;
|
|
67
|
+
current = {
|
|
68
|
+
active: true,
|
|
69
|
+
trip_id: clean(event.trip_id, 100),
|
|
70
|
+
destination: clean(event.destination, 300),
|
|
71
|
+
origin: event.origin || null,
|
|
72
|
+
started_at: clean(event.occurred_at, 50) || new Date(now).toISOString(),
|
|
73
|
+
observed_at: new Date(now).toISOString(),
|
|
74
|
+
};
|
|
75
|
+
persist();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function recordMobilityQuestion(message, now = Date.now()) {
|
|
79
|
+
ensureLoaded();
|
|
80
|
+
lastQuestion = { text: clean(message), at: new Date(now).toISOString(), at_ms: now };
|
|
81
|
+
persist();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function recordMobilityResponse(action, now = Date.now()) {
|
|
85
|
+
ensureLoaded();
|
|
86
|
+
lastResponse = { action: clean(action, 40), at: new Date(now).toISOString(), at_ms: now };
|
|
87
|
+
persist();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function mobilityQuestionIsRecent(now = Date.now()) {
|
|
91
|
+
ensureLoaded();
|
|
92
|
+
return Boolean(lastQuestion && now - lastQuestion.at_ms < RECENT_QUESTION_MS);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function mobilityContext() {
|
|
96
|
+
ensureLoaded();
|
|
97
|
+
return {
|
|
98
|
+
trip: current ? { ...current } : null,
|
|
99
|
+
last_question: lastQuestion ? { text: lastQuestion.text, at: lastQuestion.at } : null,
|
|
100
|
+
last_response: lastResponse ? { action: lastResponse.action, at: lastResponse.at } : null,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function mobilityContextBlock() {
|
|
105
|
+
const state = mobilityContext();
|
|
106
|
+
if (!state.trip && !state.last_question && !state.last_response) return "";
|
|
107
|
+
const lines = [
|
|
108
|
+
"[CONTEXTO DE MOVILIDAD]",
|
|
109
|
+
state.trip
|
|
110
|
+
? `Viaje: ${state.trip.active ? "activo" : "finalizado"}; destino: ${state.trip.destination || "desconocido"}; inicio: ${state.trip.started_at || "desconocido"}.`
|
|
111
|
+
: "Viaje: sin datos.",
|
|
112
|
+
];
|
|
113
|
+
if (state.last_question) lines.push(`Última pregunta de Roby: ${state.last_question.text} (${state.last_question.at}).`);
|
|
114
|
+
if (state.last_response) lines.push(`Última respuesta del usuario: ${state.last_response.action} (${state.last_response.at}).`);
|
|
115
|
+
lines.push("Usá este estado como contexto de secretaria. No anuncies el viaje ni repitas preguntas solo porque el estado cambió.");
|
|
116
|
+
return lines.join("\n");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The identity of a proximity alert: one ERRAND, on one trip.
|
|
121
|
+
*
|
|
122
|
+
* Deliberately NOT keyed on the place. "Buy ibuprofen at a pharmacy" is one
|
|
123
|
+
* errand; the nearest pharmacy is its answer, not thirteen separate reminders.
|
|
124
|
+
* A drive through Bariloche found fifteen matching shops for two tasks and sent
|
|
125
|
+
* eight Telegram messages in ninety seconds — the per-place key was the bug.
|
|
126
|
+
* Once the owner has been told where to stop for something, they have been
|
|
127
|
+
* told.
|
|
128
|
+
*/
|
|
129
|
+
export function mobilityAlertKey(tripId, taskId) {
|
|
130
|
+
return `${clean(tripId, 100)}|${clean(taskId, 100)}`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Has this errand already been announced on this trip?
|
|
135
|
+
*
|
|
136
|
+
* One exception, and it is the owner's own doing: an alert answered "next"
|
|
137
|
+
* ("avisar en la siguiente") does not count as having announced the errand. It
|
|
138
|
+
* declined ONE shop, not the errand — so the errand is owed another reminder if
|
|
139
|
+
* a different shop that satisfies it turns up later in the drive.
|
|
140
|
+
*
|
|
141
|
+
* That does not re-open the spam this key exists to close. The default is
|
|
142
|
+
* unchanged: told once, done. Every extra reminder past the first costs a
|
|
143
|
+
* deliberate button press, one at a time, and only ever names a place the owner
|
|
144
|
+
* has not already waved past (see declinedMobilityPlaces).
|
|
145
|
+
*/
|
|
146
|
+
export function mobilityAlertFired(tripId, taskId) {
|
|
147
|
+
ensureLoaded();
|
|
148
|
+
const key = mobilityAlertKey(tripId, taskId);
|
|
149
|
+
return alerts.some((alert) => alert.key === key && alert.answer !== "next");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The shops the owner has waved past for this errand on this trip.
|
|
154
|
+
*
|
|
155
|
+
* "Avisar en la siguiente" only means something if the NEXT one is a different
|
|
156
|
+
* one: without this the re-armed errand would re-rank onto the same nearest
|
|
157
|
+
* branch and ask about it again, which is the same message twice with extra
|
|
158
|
+
* steps.
|
|
159
|
+
*
|
|
160
|
+
* Coordinates, not names: two branches of one chain share a name, and the
|
|
161
|
+
* search that produced them is the same one that will produce them again.
|
|
162
|
+
*/
|
|
163
|
+
export function declinedMobilityPlaces(tripId, taskId) {
|
|
164
|
+
ensureLoaded();
|
|
165
|
+
const key = mobilityAlertKey(tripId, taskId);
|
|
166
|
+
return alerts
|
|
167
|
+
.filter((alert) => alert.key === key && alert.answer === "next")
|
|
168
|
+
.map((alert) => ({ latitude: alert.latitude, longitude: alert.longitude, place: alert.place }));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Record an alert the daemon is about to deliver. Written BEFORE the send, so a
|
|
173
|
+
* failed Telegram call still burns the one-shot: a delivery that errored is not
|
|
174
|
+
* a reason to try the same reminder again on the next GPS sample two seconds
|
|
175
|
+
* later. Returns the stored record (with its id).
|
|
176
|
+
*/
|
|
177
|
+
export function recordMobilityAlert(alert, now = Date.now()) {
|
|
178
|
+
ensureLoaded();
|
|
179
|
+
const record = {
|
|
180
|
+
id: `mb${Math.random().toString(36).slice(2, 8)}`,
|
|
181
|
+
key: mobilityAlertKey(alert.trip_id, alert.task_id),
|
|
182
|
+
trip_id: clean(alert.trip_id, 100),
|
|
183
|
+
task_id: clean(alert.task_id, 100),
|
|
184
|
+
task: clean(alert.task, 200),
|
|
185
|
+
project_id: clean(String(alert.project_id ?? ""), 100),
|
|
186
|
+
place: clean(alert.place, 200),
|
|
187
|
+
// The exact street address. Kept on the record, not only in the rendered
|
|
188
|
+
// message, so a card rebuilt later (a phone that reconnects, the car app
|
|
189
|
+
// asking again) says the same thing the first one did.
|
|
190
|
+
address: alert.address ? clean(alert.address, 300) : null,
|
|
191
|
+
latitude: Number(alert.latitude),
|
|
192
|
+
longitude: Number(alert.longitude),
|
|
193
|
+
distance_m: Number(alert.distance_m) || 0,
|
|
194
|
+
fired_at: new Date(now).toISOString(),
|
|
195
|
+
answer: null,
|
|
196
|
+
answered_at: null,
|
|
197
|
+
followup_at: null,
|
|
198
|
+
outcome: null,
|
|
199
|
+
};
|
|
200
|
+
alerts = [...alerts, record].slice(-MAX_ALERTS);
|
|
201
|
+
persist();
|
|
202
|
+
return record;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Every recorded alert, newest last. Read-only copies. */
|
|
206
|
+
export function listMobilityAlerts() {
|
|
207
|
+
ensureLoaded();
|
|
208
|
+
return alerts.map((alert) => ({ ...alert }));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function getMobilityAlert(id) {
|
|
212
|
+
ensureLoaded();
|
|
213
|
+
const found = alerts.find((alert) => alert.id === id);
|
|
214
|
+
return found ? { ...found } : null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function updateMobilityAlert(id, patch, now = Date.now()) {
|
|
218
|
+
ensureLoaded();
|
|
219
|
+
const index = alerts.findIndex((alert) => alert.id === id);
|
|
220
|
+
if (index < 0) return null;
|
|
221
|
+
alerts[index] = { ...alerts[index], ...patch, updated_at: new Date(now).toISOString() };
|
|
222
|
+
persist();
|
|
223
|
+
return { ...alerts[index] };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Alerts the owner said yes to and has not been asked back about. This is the
|
|
228
|
+
* whole point of keeping the log: "voy" is a promise, and the assistant that
|
|
229
|
+
* never asks how it went is a notification, not a secretary.
|
|
230
|
+
*/
|
|
231
|
+
export function pendingMobilityFollowups(tripId = null) {
|
|
232
|
+
ensureLoaded();
|
|
233
|
+
return alerts
|
|
234
|
+
.filter((alert) => alert.answer === "go" && !alert.followup_at && !alert.outcome)
|
|
235
|
+
.filter((alert) => !tripId || alert.trip_id === tripId)
|
|
236
|
+
.map((alert) => ({ ...alert }));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function _resetMobilityStateForTest() {
|
|
240
|
+
ensureLoaded();
|
|
241
|
+
current = null;
|
|
242
|
+
lastQuestion = null;
|
|
243
|
+
lastResponse = null;
|
|
244
|
+
alerts = [];
|
|
245
|
+
}
|