@agentprojectcontext/apx 1.80.2 → 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 +134 -0
- 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,667 @@
|
|
|
1
|
+
// Live proximity alerts while a trip is in progress.
|
|
2
|
+
//
|
|
3
|
+
// ./osm-route.js answers ONE question, once, at trip start: "does the planned
|
|
4
|
+
// route pass anything I have a pending task for?". This module answers a
|
|
5
|
+
// different one, continuously: "am I NEAR one of those places RIGHT NOW?".
|
|
6
|
+
// They share the need vocabulary (taskNeeds) and the place search (nearbyPois)
|
|
7
|
+
// and nothing else — one is a route-geometry check against a polyline, this is
|
|
8
|
+
// a distance check against a live GPS stream.
|
|
9
|
+
//
|
|
10
|
+
// The contract this file exists to keep: ONE alert per (trip, task, place).
|
|
11
|
+
// Not one per position sample, not one per re-approach, not one more after a
|
|
12
|
+
// daemon restart. A reminder that repeats while someone is driving is worse
|
|
13
|
+
// than no reminder at all, which is why the one-shot is burned in persistent
|
|
14
|
+
// state (./state.js) BEFORE the message is sent — a failed Telegram call is
|
|
15
|
+
// not a licence to try the same reminder again two seconds later.
|
|
16
|
+
//
|
|
17
|
+
// The SECOND contract, and the reason for the plan below: a drive costs a
|
|
18
|
+
// bounded number of place searches, ideally one, and never a search per GPS
|
|
19
|
+
// sample. See ensureTripPlan().
|
|
20
|
+
import { t } from "#core/i18n/index.js";
|
|
21
|
+
import { categoryIsLocatable } from "#core/constants/task-categories.js";
|
|
22
|
+
import { activeTasks, haversineMeters, nearbyPois, taskNeeds } from "./osm-route.js";
|
|
23
|
+
import {
|
|
24
|
+
declinedMobilityPlaces,
|
|
25
|
+
listMobilityAlerts,
|
|
26
|
+
mobilityAlertFired,
|
|
27
|
+
mobilityContext,
|
|
28
|
+
} from "./state.js";
|
|
29
|
+
|
|
30
|
+
// 2 km. The owner asked to hear about a place "at one or two kilometres" —
|
|
31
|
+
// far enough to still change lanes for it, close enough that it is genuinely
|
|
32
|
+
// on the way rather than merely in the same town.
|
|
33
|
+
export const PROXIMITY_RADIUS_M = 2_000;
|
|
34
|
+
|
|
35
|
+
// Targets are places, and places don't move: they are searched once per trip.
|
|
36
|
+
// A drive that leaves the searched area by this much gets a fresh search, so a
|
|
37
|
+
// long trip still sees the shops at the far end without turning every GPS
|
|
38
|
+
// sample into a Nominatim request.
|
|
39
|
+
const RETARGET_DISTANCE_M = 8_000;
|
|
40
|
+
|
|
41
|
+
// ~13 km around the current position. Nominatim needs a box with area, and a
|
|
42
|
+
// box built from a single point has none — see boundingBox()'s padDegrees.
|
|
43
|
+
const SEARCH_PAD_DEGREES = 0.12;
|
|
44
|
+
|
|
45
|
+
// A city search can match thirty shops. The banner's list is "what is near me
|
|
46
|
+
// on this drive", not a directory — past the nearest handful nobody is
|
|
47
|
+
// detouring, and a long list is unreadable at a red light.
|
|
48
|
+
const MAX_LISTED_PLACES = 8;
|
|
49
|
+
|
|
50
|
+
// Per-trip plan, held in memory. Deliberately NOT persisted: it is a derived
|
|
51
|
+
// cache (rebuilt from tasks + one place search), while the thing that must
|
|
52
|
+
// survive a restart — which reminders were already delivered — lives in
|
|
53
|
+
// ./state.js. Two lifetimes, two homes.
|
|
54
|
+
const armed = new Map();
|
|
55
|
+
|
|
56
|
+
function finite(value) {
|
|
57
|
+
const number = Number(value);
|
|
58
|
+
return Number.isFinite(number) ? number : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function text(value, max = 160) {
|
|
62
|
+
return typeof value === "string" ? value.trim().slice(0, max) : "";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Validate an inbound GPS sample. Throws with a caller-facing message, the
|
|
67
|
+
* same shape acceptMobilityEvent() uses, so the route can answer 400 without
|
|
68
|
+
* knowing anything about coordinates.
|
|
69
|
+
*/
|
|
70
|
+
export function acceptMobilityPosition(body = {}, now = Date.now()) {
|
|
71
|
+
const latitude = finite(body.latitude);
|
|
72
|
+
const longitude = finite(body.longitude);
|
|
73
|
+
if (latitude == null || longitude == null) throw new Error("latitude and longitude required");
|
|
74
|
+
if (latitude < -90 || latitude > 90) throw new Error("latitude out of range");
|
|
75
|
+
if (longitude < -180 || longitude > 180) throw new Error("longitude out of range");
|
|
76
|
+
const tripId = text(body.trip_id, 100);
|
|
77
|
+
if (!tripId) throw new Error("trip_id required");
|
|
78
|
+
return {
|
|
79
|
+
trip_id: tripId,
|
|
80
|
+
latitude,
|
|
81
|
+
longitude,
|
|
82
|
+
accuracy_m: finite(body.accuracy_m),
|
|
83
|
+
speed_mps: finite(body.speed_mps),
|
|
84
|
+
heading_deg: finite(body.heading_deg),
|
|
85
|
+
occurred_at: text(body.occurred_at, 50) || new Date(now).toISOString(),
|
|
86
|
+
source: text(body.source, 40) || "android",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Every place worth watching on this trip: one entry per (pending task × place
|
|
92
|
+
* that satisfies it). Built from the tasks themselves — a task that names no
|
|
93
|
+
* physical errand produces no target, so a trip with nothing to buy is silent
|
|
94
|
+
* without a single network call.
|
|
95
|
+
*/
|
|
96
|
+
export async function buildTripTargets(position, ctx, fetchFn = fetch) {
|
|
97
|
+
const open = activeTasks(ctx.projects);
|
|
98
|
+
|
|
99
|
+
// A task that already carries its own pinned place is the cheap path and the
|
|
100
|
+
// exact one: no geocoding, no place search, no model reading the title to
|
|
101
|
+
// guess what kind of errand it is. That is what a `trip` category with a
|
|
102
|
+
// location is FOR — see core/constants/task-categories.js.
|
|
103
|
+
const pinned = [];
|
|
104
|
+
const unpinned = [];
|
|
105
|
+
for (const task of open) {
|
|
106
|
+
const spot = task.location;
|
|
107
|
+
if (categoryIsLocatable(task.category) && spot?.latitude != null && spot?.longitude != null) {
|
|
108
|
+
pinned.push({
|
|
109
|
+
task_id: task.id,
|
|
110
|
+
task: task.title || "",
|
|
111
|
+
project_id: task.project_id ?? task.projectId ?? "",
|
|
112
|
+
need_id: "pinned",
|
|
113
|
+
place: spot.place || spot.address || task.title || "",
|
|
114
|
+
// The exact street address, when the task carries one. It rides all the
|
|
115
|
+
// way to the card and the spoken alert.
|
|
116
|
+
address: spot.address || null,
|
|
117
|
+
latitude: spot.latitude,
|
|
118
|
+
longitude: spot.longitude,
|
|
119
|
+
...(Number.isFinite(spot.radius_m) ? { radius_m: spot.radius_m } : {}),
|
|
120
|
+
});
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
unpinned.push(task);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Everything else still goes through the need vocabulary: a title that names
|
|
127
|
+
// a pharmacy, with no place attached, is worth searching for.
|
|
128
|
+
const rows = unpinned
|
|
129
|
+
.map((task) => ({ task, needs: taskNeeds(task) }))
|
|
130
|
+
.filter((row) => row.needs.length);
|
|
131
|
+
if (!rows.length) return pinned;
|
|
132
|
+
|
|
133
|
+
const needs = [...new Map(
|
|
134
|
+
rows.flatMap((row) => row.needs).map((need) => [`${need.id}:${need.query}`, need])
|
|
135
|
+
).values()];
|
|
136
|
+
const places = await nearbyPois(
|
|
137
|
+
{ points: [{ latitude: position.latitude, longitude: position.longitude }] },
|
|
138
|
+
needs,
|
|
139
|
+
fetchFn,
|
|
140
|
+
{ padDegrees: SEARCH_PAD_DEGREES }
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const targets = [...pinned];
|
|
144
|
+
for (const row of rows) {
|
|
145
|
+
for (const place of places) {
|
|
146
|
+
if (!row.needs.some((need) => need.id === place.tags?.need_id)) continue;
|
|
147
|
+
targets.push({
|
|
148
|
+
task_id: row.task.id,
|
|
149
|
+
task: row.task.title || "",
|
|
150
|
+
project_id: row.task.project_id ?? row.task.projectId ?? "",
|
|
151
|
+
need_id: place.tags.need_id,
|
|
152
|
+
place: place.name,
|
|
153
|
+
address: place.address || null,
|
|
154
|
+
latitude: place.latitude,
|
|
155
|
+
longitude: place.longitude,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return targets;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* What the open errands are, as one short string. This is the ONLY thing that
|
|
164
|
+
* expires a plan besides distance, and it replaced a blind ten-minute timer.
|
|
165
|
+
*
|
|
166
|
+
* The timer was the wrong instrument: on a drive where nothing changes it paid
|
|
167
|
+
* for a search every ten minutes to re-learn the same answer, and on the case
|
|
168
|
+
* it existed for — a task added from the phone mid-drive — it still left the
|
|
169
|
+
* new errand invisible for up to ten minutes. Reading the task list is a local
|
|
170
|
+
* file read: no network, no API bill, no model. So we read it every sample and
|
|
171
|
+
* search only when the answer would actually differ.
|
|
172
|
+
*/
|
|
173
|
+
function errandFingerprint(open) {
|
|
174
|
+
return open
|
|
175
|
+
.filter((task) => categoryIsLocatable(task.category) || taskNeeds(task).length)
|
|
176
|
+
.map((task) => {
|
|
177
|
+
const spot = task.location;
|
|
178
|
+
const pin = spot?.latitude != null ? `@${spot.latitude},${spot.longitude}` : "";
|
|
179
|
+
return `${task.id}${pin}`;
|
|
180
|
+
})
|
|
181
|
+
.sort()
|
|
182
|
+
.join("|");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Group flat targets into one entry per ERRAND, each holding every place that
|
|
187
|
+
* could satisfy it.
|
|
188
|
+
*
|
|
189
|
+
* "Buy bread" is not a place, it is a choice between places — the corner shop,
|
|
190
|
+
* the supermarket, the other supermarket. Keeping the candidates instead of
|
|
191
|
+
* collapsing them at search time is what lets the choice follow the driver:
|
|
192
|
+
* re-ranking them as the car moves is pure arithmetic on numbers we already
|
|
193
|
+
* have, while re-deciding by searching again is a network call.
|
|
194
|
+
*/
|
|
195
|
+
function toErrands(targets, previous = new Map()) {
|
|
196
|
+
const errands = new Map();
|
|
197
|
+
for (const target of targets) {
|
|
198
|
+
let errand = errands.get(target.task_id);
|
|
199
|
+
if (!errand) {
|
|
200
|
+
const before = previous.get(target.task_id);
|
|
201
|
+
errand = {
|
|
202
|
+
task_id: target.task_id,
|
|
203
|
+
task: target.task,
|
|
204
|
+
project_id: target.project_id,
|
|
205
|
+
candidates: [],
|
|
206
|
+
chosen: null,
|
|
207
|
+
// A lock survives a re-search: the owner already said WHERE, and a
|
|
208
|
+
// fresh list of shops is not a reason to change the answer.
|
|
209
|
+
locked: Boolean(before?.locked),
|
|
210
|
+
lock_reason: before?.lock_reason || null,
|
|
211
|
+
};
|
|
212
|
+
errands.set(target.task_id, errand);
|
|
213
|
+
}
|
|
214
|
+
errand.candidates.push(target);
|
|
215
|
+
}
|
|
216
|
+
// Re-attach the place a locked errand was locked TO, even if this search did
|
|
217
|
+
// not return it. Losing it would silently unlock the errand.
|
|
218
|
+
for (const [taskId, before] of previous) {
|
|
219
|
+
const errand = errands.get(taskId);
|
|
220
|
+
if (!errand || !before.locked || !before.chosen) continue;
|
|
221
|
+
if (!errand.candidates.some((candidate) => sameSpot(candidate, before.chosen))) {
|
|
222
|
+
errand.candidates.push(before.chosen);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
for (const errand of errands.values()) {
|
|
226
|
+
// A single candidate IS the decision. There is nothing left to re-rank, so
|
|
227
|
+
// this errand never costs another search for the rest of the trip — which
|
|
228
|
+
// is the pinned-task path and, in a small town, most of the others too.
|
|
229
|
+
if (errand.candidates.length === 1 && !errand.locked) {
|
|
230
|
+
errand.locked = true;
|
|
231
|
+
errand.lock_reason = errand.candidates[0].need_id === "pinned" ? "pinned" : "only-candidate";
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return errands;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function sameSpot(a, b) {
|
|
238
|
+
return a && b && a.latitude === b.latitude && a.longitude === b.longitude;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Point each errand at its nearest candidate. Arithmetic only — never a call.
|
|
243
|
+
*
|
|
244
|
+
* "Nearest" means nearest of the ones still on offer: a shop the owner answered
|
|
245
|
+
* "avisar en la siguiente" about is out of the running for the rest of the trip,
|
|
246
|
+
* so the errand moves on to the next one instead of asking about the same
|
|
247
|
+
* branch again.
|
|
248
|
+
*/
|
|
249
|
+
function rank(errands, position) {
|
|
250
|
+
for (const errand of errands.values()) {
|
|
251
|
+
const declined = declinedMobilityPlaces(position.trip_id, errand.task_id);
|
|
252
|
+
const declinedSpot = (spot) => declined.some((gone) => sameSpot(gone, spot));
|
|
253
|
+
// Waving past the shop an errand was SETTLED on is the one answer that
|
|
254
|
+
// re-opens the question: the lock said "this branch", and the owner has
|
|
255
|
+
// just said "not this branch".
|
|
256
|
+
if (errand.locked && errand.chosen && declinedSpot(errand.chosen)) {
|
|
257
|
+
errand.locked = false;
|
|
258
|
+
errand.lock_reason = null;
|
|
259
|
+
}
|
|
260
|
+
// A locked errand keeps its PLACE. This is the owner's "unless we already
|
|
261
|
+
// have one single place to go": once the answer is settled, driving past
|
|
262
|
+
// a closer branch does not re-open the question.
|
|
263
|
+
//
|
|
264
|
+
// Its DISTANCE is not settled, though, and must be re-measured like anyone
|
|
265
|
+
// else's: the car is driving TOWARD that place, and how far away it is now
|
|
266
|
+
// is the whole basis of the proximity check. Keeping the number the lock
|
|
267
|
+
// was taken with froze it at the first sample of the trip — and since
|
|
268
|
+
// prepareTripPlan warms the plan from the ORIGIN, that number is the
|
|
269
|
+
// distance from where the drive started. A pinned errand (locked on sight,
|
|
270
|
+
// by definition) could therefore never come into range: you arrive at the
|
|
271
|
+
// shop and evaluateMobilityPosition is still comparing the radius against
|
|
272
|
+
// how far the shop is from your house.
|
|
273
|
+
if (errand.locked && errand.chosen) {
|
|
274
|
+
errand.chosen = {
|
|
275
|
+
...errand.chosen,
|
|
276
|
+
distance_m: Math.round(haversineMeters(position, errand.chosen)),
|
|
277
|
+
};
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
let best = null;
|
|
281
|
+
for (const candidate of errand.candidates) {
|
|
282
|
+
if (declinedSpot(candidate)) continue;
|
|
283
|
+
const distance_m = Math.round(haversineMeters(position, candidate));
|
|
284
|
+
if (!best || distance_m < best.distance_m) best = { ...candidate, distance_m };
|
|
285
|
+
}
|
|
286
|
+
// No candidate left means every shop that could satisfy this errand has
|
|
287
|
+
// been waved past. `chosen: null` is silence for the rest of the trip —
|
|
288
|
+
// "avisar en la siguiente" with no siguiente is just "hoy no", and saying
|
|
289
|
+
// so out loud would be arguing with an answer the owner already gave.
|
|
290
|
+
errand.chosen = best;
|
|
291
|
+
}
|
|
292
|
+
return errands;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* The trip's plan: every open errand, the places that could satisfy it, and
|
|
297
|
+
* which one it is currently pointed at.
|
|
298
|
+
*
|
|
299
|
+
* A search happens when — and only when — the plan cannot answer from what it
|
|
300
|
+
* already holds:
|
|
301
|
+
*
|
|
302
|
+
* 1. there is no plan yet (once per trip, at boarding);
|
|
303
|
+
* 2. the car has left the searched area by RETARGET_DISTANCE_M, so the
|
|
304
|
+
* shops at the far end of a long drive were never in the first answer;
|
|
305
|
+
* 3. the open errands changed, which is a local file read away.
|
|
306
|
+
*
|
|
307
|
+
* And never at all once every errand is locked: at that point the plan is a
|
|
308
|
+
* finished decision and more searching cannot change it. A normal drive with a
|
|
309
|
+
* settled shopping list therefore costs ONE search — or zero, when the tasks
|
|
310
|
+
* carry their own pins.
|
|
311
|
+
*/
|
|
312
|
+
export async function ensureTripPlan(position, ctx, fetchFn = fetch, now = Date.now()) {
|
|
313
|
+
const cached = armed.get(position.trip_id);
|
|
314
|
+
const open = activeTasks(ctx.projects);
|
|
315
|
+
const fingerprint = errandFingerprint(open);
|
|
316
|
+
if (cached) {
|
|
317
|
+
const settled = cached.errands.size > 0 &&
|
|
318
|
+
[...cached.errands.values()].every((errand) => errand.locked);
|
|
319
|
+
const nearby = haversineMeters(cached.searchedAt, position) < RETARGET_DISTANCE_M;
|
|
320
|
+
if (settled || (nearby && cached.fingerprint === fingerprint)) {
|
|
321
|
+
cached.lastPosition = { latitude: position.latitude, longitude: position.longitude };
|
|
322
|
+
return rank(cached.errands, position);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
let targets = [];
|
|
326
|
+
try {
|
|
327
|
+
targets = await buildTripTargets(position, ctx, fetchFn);
|
|
328
|
+
} catch (error) {
|
|
329
|
+
targets = [];
|
|
330
|
+
if (ctx?.log) ctx.log(`[mobility] target search failed: ${error?.message || error}`);
|
|
331
|
+
}
|
|
332
|
+
const errands = toErrands(targets, cached?.errands || new Map());
|
|
333
|
+
armed.set(position.trip_id, {
|
|
334
|
+
searchedAt: { latitude: position.latitude, longitude: position.longitude },
|
|
335
|
+
searchedAtMs: now,
|
|
336
|
+
fingerprint,
|
|
337
|
+
lastPosition: { latitude: position.latitude, longitude: position.longitude },
|
|
338
|
+
errands,
|
|
339
|
+
});
|
|
340
|
+
return rank(errands, position);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* The flat target list, for callers that want places rather than decisions.
|
|
345
|
+
* Every candidate of every errand — an errand pointed at one shop is still
|
|
346
|
+
* WATCHING the others until it is locked.
|
|
347
|
+
*/
|
|
348
|
+
export async function ensureTripTargets(position, ctx, fetchFn = fetch, now = Date.now()) {
|
|
349
|
+
const errands = await ensureTripPlan(position, ctx, fetchFn, now);
|
|
350
|
+
return [...errands.values()].flatMap((errand) => errand.candidates);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Settle an errand on the place the owner just accepted, and stop looking.
|
|
355
|
+
*
|
|
356
|
+
* Pressing "voy" is the strongest possible answer to "which of these shops?",
|
|
357
|
+
* and it arrives from the chip handler long after the search that offered the
|
|
358
|
+
* choice. Without this the next re-search would re-rank the errand and could
|
|
359
|
+
* point it somewhere else while the driver is on their way to the first one.
|
|
360
|
+
*/
|
|
361
|
+
export function lockTripErrand(tripId, taskId, reason = "accepted") {
|
|
362
|
+
const entry = armed.get(tripId);
|
|
363
|
+
const errand = entry?.errands?.get(taskId);
|
|
364
|
+
if (!errand) return false;
|
|
365
|
+
errand.locked = true;
|
|
366
|
+
errand.lock_reason = reason;
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* The errand a surface is pointing at, as an alert-shaped record.
|
|
372
|
+
*
|
|
373
|
+
* The phone's list offers the same four choices as the Telegram card, and
|
|
374
|
+
* "voy" pressed there has to mean exactly what "voy" pressed in Telegram
|
|
375
|
+
* means — same one-shot, same follow-up after the trip. So it resolves to the
|
|
376
|
+
* same shape recordMobilityAlert() stores, rather than growing a second,
|
|
377
|
+
* parallel notion of "the owner answered".
|
|
378
|
+
*/
|
|
379
|
+
export function tripErrand(tripId, taskId) {
|
|
380
|
+
const errand = armed.get(tripId)?.errands?.get(taskId);
|
|
381
|
+
if (!errand?.chosen) return null;
|
|
382
|
+
return {
|
|
383
|
+
...errand.chosen,
|
|
384
|
+
trip_id: tripId,
|
|
385
|
+
task_id: errand.task_id,
|
|
386
|
+
task: errand.task,
|
|
387
|
+
project_id: errand.project_id,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Everything this trip is watching, for a surface that wants to SHOW it rather
|
|
393
|
+
* than be interrupted by it — the phone's trip list. One row per errand, not
|
|
394
|
+
* per place: the driver picks between errands, and the shop is the plan's
|
|
395
|
+
* business. Each row says whether the owner has already been asked and what
|
|
396
|
+
* they answered, so the list reads as state and not as a second round of
|
|
397
|
+
* reminders.
|
|
398
|
+
*/
|
|
399
|
+
export function tripPlaces(tripId) {
|
|
400
|
+
const entry = armed.get(tripId);
|
|
401
|
+
if (!entry?.errands?.size) return [];
|
|
402
|
+
const from = entry.lastPosition || entry.searchedAt;
|
|
403
|
+
const destination = mobilityContext().trip?.destination || "";
|
|
404
|
+
const answers = new Map(
|
|
405
|
+
listMobilityAlerts()
|
|
406
|
+
.filter((alert) => alert.trip_id === tripId)
|
|
407
|
+
.map((alert) => [alert.task_id, alert])
|
|
408
|
+
);
|
|
409
|
+
return [...entry.errands.values()]
|
|
410
|
+
.filter((errand) => errand.chosen)
|
|
411
|
+
.map((errand) => {
|
|
412
|
+
const alert = answers.get(errand.task_id);
|
|
413
|
+
const chosen = errand.chosen;
|
|
414
|
+
return {
|
|
415
|
+
task_id: errand.task_id,
|
|
416
|
+
task: errand.task,
|
|
417
|
+
place: chosen.place,
|
|
418
|
+
latitude: chosen.latitude,
|
|
419
|
+
longitude: chosen.longitude,
|
|
420
|
+
distance_m: Math.round(haversineMeters(from, chosen)),
|
|
421
|
+
maps_url: navigateUrl(chosen),
|
|
422
|
+
// Both links the Telegram card offers, so a surface reading this list
|
|
423
|
+
// can present the same two choices without knowing how to build a
|
|
424
|
+
// Maps URL. Without a destination the second degrades to plain
|
|
425
|
+
// navigation — see addToRouteUrl.
|
|
426
|
+
add_stop_url: addToRouteUrl(chosen, destination),
|
|
427
|
+
// How many shops could still satisfy this errand. 1 means the choice
|
|
428
|
+
// is made; more means the list is showing the nearest of several.
|
|
429
|
+
options: errand.candidates.length,
|
|
430
|
+
locked: Boolean(errand.locked),
|
|
431
|
+
alerted: Boolean(alert),
|
|
432
|
+
answer: alert?.answer || null,
|
|
433
|
+
};
|
|
434
|
+
})
|
|
435
|
+
.sort((a, b) => a.distance_m - b.distance_m)
|
|
436
|
+
.slice(0, MAX_LISTED_PLACES);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* The alerts this position newly earns — usually none. AT MOST ONE PER ERRAND:
|
|
441
|
+
* a task that matches thirteen pharmacies gets the nearest one, not thirteen
|
|
442
|
+
* cards. Candidates are returned un-recorded so the caller burns the one-shot
|
|
443
|
+
* only on the ones it actually tries to deliver; recording every match here
|
|
444
|
+
* silently spent reminders that were never sent.
|
|
445
|
+
*/
|
|
446
|
+
export async function evaluateMobilityPosition(position, ctx, fetchFn = fetch) {
|
|
447
|
+
const errands = await ensureTripPlan(position, ctx, fetchFn);
|
|
448
|
+
const due = [];
|
|
449
|
+
for (const errand of errands.values()) {
|
|
450
|
+
if (!errand.chosen) continue;
|
|
451
|
+
if (mobilityAlertFired(position.trip_id, errand.task_id)) continue;
|
|
452
|
+
const chosen = errand.chosen;
|
|
453
|
+
const distance_m = chosen.distance_m ?? Math.round(haversineMeters(position, chosen));
|
|
454
|
+
// A task may set its own idea of "there" — a pharmacy you would detour two
|
|
455
|
+
// kilometres for is not a bakery you would only stop at if you were passing.
|
|
456
|
+
if (distance_m > (chosen.radius_m || PROXIMITY_RADIUS_M)) continue;
|
|
457
|
+
due.push({ ...chosen, trip_id: position.trip_id, distance_m });
|
|
458
|
+
}
|
|
459
|
+
// Nearest first: if two errands land on the same sample, the one you are
|
|
460
|
+
// about to drive past is the one that matters.
|
|
461
|
+
return due.sort((a, b) => a.distance_m - b.distance_m);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** Google Maps deep link that starts turn-by-turn navigation to the place. */
|
|
465
|
+
export function navigateUrl(alert) {
|
|
466
|
+
return `https://www.google.com/maps/dir/?api=1&destination=${alert.latitude},${alert.longitude}&travelmode=driving`;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Google Maps deep link that keeps the current destination and inserts the
|
|
471
|
+
* place as a stop along the way. Without a known destination there is no route
|
|
472
|
+
* to add to, so it degrades to plain navigation rather than inventing one.
|
|
473
|
+
*/
|
|
474
|
+
export function addToRouteUrl(alert, destination = "") {
|
|
475
|
+
const stop = `${alert.latitude},${alert.longitude}`;
|
|
476
|
+
if (!destination) return navigateUrl(alert);
|
|
477
|
+
return `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(destination)}` +
|
|
478
|
+
`&waypoints=${stop}&travelmode=driving`;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** One line, ending in exactly one full stop. */
|
|
482
|
+
function sentence(line) {
|
|
483
|
+
return `${line.replace(/[.\s]+$/, "")}.`;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function distanceLabel(meters) {
|
|
487
|
+
return meters >= 1000 ? `${(meters / 1000).toFixed(1)} km` : `${meters} m`;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* The same distance, for a speech engine.
|
|
492
|
+
*
|
|
493
|
+
* "600 m" and "1.4 km" are right on a screen and wrong out loud: the unit is
|
|
494
|
+
* read as a letter or expanded inconsistently, and the decimal POINT is an
|
|
495
|
+
* English convention that a Spanish voice reads as "punto" in the middle of a
|
|
496
|
+
* number. Spelled out, and with the comma this language actually uses, it comes
|
|
497
|
+
* out as one spoken quantity instead of three tokens.
|
|
498
|
+
*/
|
|
499
|
+
function spokenDistance(meters, lang) {
|
|
500
|
+
if (meters >= 1000) {
|
|
501
|
+
const km = (meters / 1000).toFixed(1);
|
|
502
|
+
return `${lang === "en" ? km : km.replace(".", ",")} ${t("mobility.unit_km", { lang })}`;
|
|
503
|
+
}
|
|
504
|
+
return `${meters} ${t("mobility.unit_m", { lang })}`;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* The reminder itself — read at 60 km/h, or heard over the car speakers.
|
|
509
|
+
*
|
|
510
|
+
* THE ADDRESS IS A LINE OF ITS OWN. "Estás cerca de Farmacia del Puente" names
|
|
511
|
+
* a shop; it does not tell you which corner it is on, and at the wheel that is
|
|
512
|
+
* the difference between taking the turn and driving past. It is omitted only
|
|
513
|
+
* when we genuinely do not have one — never faked from the place name.
|
|
514
|
+
*/
|
|
515
|
+
export function proximityMessage(alert, lang = "es") {
|
|
516
|
+
return [
|
|
517
|
+
// THE ERRAND LEADS. The place is where, the distance is how far, but the
|
|
518
|
+
// errand is WHY any of it is on screen — and it used to be the third line,
|
|
519
|
+
// after two the driver had to read before learning whether this was worth
|
|
520
|
+
// looking at. Whatever is read first has to answer "do I care?".
|
|
521
|
+
alert.task ? `📍 ${alert.task}` : `📍 ${alert.place}`,
|
|
522
|
+
alert.task
|
|
523
|
+
? t("mobility.near", { lang, vars: { place: alert.place, distance: distanceLabel(alert.distance_m) } })
|
|
524
|
+
: t("mobility.near_bare", { lang, vars: { distance: distanceLabel(alert.distance_m) } }),
|
|
525
|
+
alert.address ? `${t("mobility.address", { lang })}: ${alert.address}` : "",
|
|
526
|
+
].filter(Boolean).join("\n");
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* The four answers, in the order they are offered.
|
|
531
|
+
*
|
|
532
|
+
* The ids are the ones the Telegram callback handler already speaks
|
|
533
|
+
* (`apx:mobility:<action>`), so a tap on the car card and a tap on a Telegram
|
|
534
|
+
* chip land on the same branch. `next` is "tell me at the following shop";
|
|
535
|
+
* `skip` puts the errand away for the rest of the trip — the one-shot in
|
|
536
|
+
* state.js (mobilityAlertFired) is what makes that stick, since any answer
|
|
537
|
+
* other than `next` counts as announced.
|
|
538
|
+
*/
|
|
539
|
+
export const PROXIMITY_ACTIONS = Object.freeze([
|
|
540
|
+
{ id: "navigate", label: "mobility.navigate" },
|
|
541
|
+
{ id: "add_stop", label: "mobility.add_stop" },
|
|
542
|
+
{ id: "next", label: "mobility.later" },
|
|
543
|
+
{ id: "skip", label: "mobility.dismiss" },
|
|
544
|
+
]);
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* The alert as a NATIVE card: what Android draws on the phone and on the head
|
|
548
|
+
* unit, and what the Assistant reads aloud.
|
|
549
|
+
*
|
|
550
|
+
* Structured, not a rendered string, for two reasons. The car needs the pieces
|
|
551
|
+
* separately — a title line, a spoken body, four labelled actions, two Maps
|
|
552
|
+
* URIs — and building them by parsing a message back apart is how a card ends
|
|
553
|
+
* up saying something the message did not.
|
|
554
|
+
*
|
|
555
|
+
* NOTHING HERE CARRIES AN EMOJI. Android Auto hands the card to Assistant and
|
|
556
|
+
* it is read out loud: a 📍 is pronounced as its Unicode name in the middle of
|
|
557
|
+
* a sentence, and "check mark button navegar" is not a thing anyone wants to
|
|
558
|
+
* hear at 110 km/h. The Telegram card above keeps its glyphs — it is read with
|
|
559
|
+
* the eyes, and there they help.
|
|
560
|
+
*/
|
|
561
|
+
export function proximityCard(alert, { destination = "", lang = "es" } = {}) {
|
|
562
|
+
const distance = distanceLabel(alert.distance_m);
|
|
563
|
+
// SPOKEN, NOT LABELLED. The Telegram copy prefixes its lines with "Dirección:"
|
|
564
|
+
// and that is right for something read with the eyes — the label is how you
|
|
565
|
+
// skip to the part you want. Out loud it is the opposite: Assistant says the
|
|
566
|
+
// word "Dirección" and then the address, so every card costs the driver two
|
|
567
|
+
// filler words per line before the content. The errand leads here too.
|
|
568
|
+
const spoken = spokenDistance(alert.distance_m, lang);
|
|
569
|
+
const lines = [
|
|
570
|
+
alert.task || alert.place,
|
|
571
|
+
alert.task
|
|
572
|
+
? t("mobility.near", { lang, vars: { place: alert.place, distance: spoken } })
|
|
573
|
+
: t("mobility.near_bare", { lang, vars: { distance: spoken } }),
|
|
574
|
+
alert.address || "",
|
|
575
|
+
].filter(Boolean);
|
|
576
|
+
return {
|
|
577
|
+
id: alert.id,
|
|
578
|
+
trip_id: alert.trip_id,
|
|
579
|
+
task_id: alert.task_id,
|
|
580
|
+
// The errand titles the card, for the same reason it leads the body: on a
|
|
581
|
+
// list of rows at a red light, "Comprar ibuprofeno" identifies which card
|
|
582
|
+
// this is and "Farmacia del Puente" does not — you have several errands,
|
|
583
|
+
// not several pharmacies.
|
|
584
|
+
title: alert.task || alert.place,
|
|
585
|
+
place: alert.place,
|
|
586
|
+
address: alert.address || null,
|
|
587
|
+
distance_m: alert.distance_m,
|
|
588
|
+
distance_label: distance,
|
|
589
|
+
task: alert.task || null,
|
|
590
|
+
latitude: alert.latitude,
|
|
591
|
+
longitude: alert.longitude,
|
|
592
|
+
// One block, already in reading (and speaking) order. Sentences are
|
|
593
|
+
// normalised to exactly one full stop each: "mobility.near" ends with one
|
|
594
|
+
// of its own and the others do not, so a plain join produced "(600 m).."
|
|
595
|
+
// — which a speech engine reads as a pause twice as long as it should be.
|
|
596
|
+
body: lines.map(sentence).join(" "),
|
|
597
|
+
navigate_url: navigateUrl(alert),
|
|
598
|
+
add_stop_url: addToRouteUrl(alert, destination),
|
|
599
|
+
actions: PROXIMITY_ACTIONS.map(({ id, label }) => ({ id, label: t(label, { lang }) })),
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* The chips. Two navigation links (which Telegram opens straight into Maps)
|
|
605
|
+
* and three answers, because "do I detour for this?" is the only question the
|
|
606
|
+
* driver actually has to answer right now. The follow-up — "did you get it?" —
|
|
607
|
+
* is asked after the trip, from the answer recorded here.
|
|
608
|
+
*
|
|
609
|
+
* The second answer is "avisar en la siguiente", not "hoy no". Waving past one
|
|
610
|
+
* shop is not the same as dropping the errand, and treating it as the same is
|
|
611
|
+
* what the button used to do: decline the branch you happen to be passing and
|
|
612
|
+
* the whole task went quiet for the rest of the drive, including at the three
|
|
613
|
+
* other branches you were about to drive past. So it declines the PLACE, and
|
|
614
|
+
* the errand is still owed a reminder at the next shop that satisfies it.
|
|
615
|
+
*
|
|
616
|
+
* When there is no next shop it degrades to exactly what "hoy no" did — silence
|
|
617
|
+
* for the rest of the trip — so nothing is lost by having only these two.
|
|
618
|
+
* Dropping every reminder for the day is a different question with its own
|
|
619
|
+
* button, on the trip card (trip-event.js).
|
|
620
|
+
*/
|
|
621
|
+
export function proximityKeyboard(alert, { destination = "", lang = "es" } = {}) {
|
|
622
|
+
return {
|
|
623
|
+
inline_keyboard: [
|
|
624
|
+
[
|
|
625
|
+
{ text: `🧭 ${t("mobility.navigate", { lang })}`, url: navigateUrl(alert) },
|
|
626
|
+
{ text: `➕ ${t("mobility.add_stop", { lang })}`, url: addToRouteUrl(alert, destination) },
|
|
627
|
+
],
|
|
628
|
+
[
|
|
629
|
+
{ text: `✅ ${t("mobility.going", { lang })}`, callback_data: `apx:mobility:go:${alert.id}` },
|
|
630
|
+
{ text: `🔁 ${t("mobility.later", { lang })}`, callback_data: `apx:mobility:next:${alert.id}` },
|
|
631
|
+
],
|
|
632
|
+
// "No ahora" drops the ERRAND for the rest of the drive, where the row
|
|
633
|
+
// above only moves it to the next branch. It is on its own line because
|
|
634
|
+
// it is the only irreversible one of the four, and a thumb at a red light
|
|
635
|
+
// should not find it next to the answer that means "ask me again".
|
|
636
|
+
[
|
|
637
|
+
{ text: `🚫 ${t("mobility.dismiss", { lang })}`, callback_data: `apx:mobility:skip:${alert.id}` },
|
|
638
|
+
],
|
|
639
|
+
],
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/** The question asked once the driving is over, for every "voy" that went out. */
|
|
644
|
+
export function followupMessage(alert, lang = "es") {
|
|
645
|
+
const question = t("mobility.followup", { lang, vars: { place: alert.place } });
|
|
646
|
+
return alert.task ? `${question}\n${t("mobility.task", { lang })}: ${alert.task}` : question;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export function followupKeyboard(alert, lang = "es") {
|
|
650
|
+
return {
|
|
651
|
+
inline_keyboard: [
|
|
652
|
+
[
|
|
653
|
+
{ text: `✅ ${t("mobility.done", { lang })}`, callback_data: `apx:mobility:done:${alert.id}` },
|
|
654
|
+
{ text: `🕓 ${t("mobility.still_open", { lang })}`, callback_data: `apx:mobility:open:${alert.id}` },
|
|
655
|
+
],
|
|
656
|
+
],
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/** Drop a finished trip's plan. Called when the trip ends. */
|
|
661
|
+
export function releaseTripTargets(tripId) {
|
|
662
|
+
armed.delete(tripId);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export function _resetMobilityGeofencesForTest() {
|
|
666
|
+
armed.clear();
|
|
667
|
+
}
|