@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,136 @@
|
|
|
1
|
+
// Board columns: ONE catalog, shared by every project; each project picks which
|
|
2
|
+
// of them it shows.
|
|
3
|
+
//
|
|
4
|
+
// This settles a real tension in how one person uses one tool. Dev work wants
|
|
5
|
+
// pending → running → in review → blocked; a personal list wants pending and
|
|
6
|
+
// done and nothing else. "Every project the same" makes the second list carry
|
|
7
|
+
// four columns it never uses; "every project its own" means `qa` on one board
|
|
8
|
+
// is a different thing from `qa` on the next, and an agent told to "move it to
|
|
9
|
+
// QA" has to ask which QA. So: the VOCABULARY is global and editable, and the
|
|
10
|
+
// SUBSET is per project.
|
|
11
|
+
//
|
|
12
|
+
// A column is a task `status`. That is why this is cheap: nothing in the daemon
|
|
13
|
+
// branches on the value of `status` — it is a validation whitelist, a label, and
|
|
14
|
+
// something to group by. Making the whitelist configurable is the whole feature.
|
|
15
|
+
//
|
|
16
|
+
// `done` is never in the catalog. It is a task's `state`, not its status, and it
|
|
17
|
+
// is the one column every board ends with — dragging a card there closes the
|
|
18
|
+
// task, dragging it out reopens it. Making it configurable would let someone
|
|
19
|
+
// build a board with no way to finish anything.
|
|
20
|
+
import { TASK_STATUSES, DEFAULT_TASK_STATUS } from "#core/stores/tasks.js";
|
|
21
|
+
|
|
22
|
+
/** The terminal column every board gets, appended after the configured ones. */
|
|
23
|
+
export const DONE_COLUMN = "done";
|
|
24
|
+
|
|
25
|
+
/** Shipped catalog — exactly the statuses that existed before this was configurable. */
|
|
26
|
+
export const DEFAULT_TASK_COLUMNS = Object.freeze(
|
|
27
|
+
TASK_STATUSES.map((id) => Object.freeze({ id, label: null })),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const ID_RE = /^[a-z0-9][a-z0-9_-]{0,31}$/;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The on-enter automation, or nothing. An `agent` is required — an instruction
|
|
34
|
+
* with nobody to carry it out is not a half-configured hook, it is no hook.
|
|
35
|
+
*/
|
|
36
|
+
function normalizeHook(value) {
|
|
37
|
+
const agent = String(value?.agent || "").trim().toLowerCase();
|
|
38
|
+
if (!agent || !ID_RE.test(agent)) return {};
|
|
39
|
+
const instruction = typeof value?.instruction === "string" ? value.instruction.trim() : "";
|
|
40
|
+
return { on_enter: { agent, instruction: instruction || null } };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A usable column id: slug-shaped, and never the reserved terminal one. */
|
|
44
|
+
export function isColumnId(value) {
|
|
45
|
+
const id = String(value || "").trim().toLowerCase();
|
|
46
|
+
return ID_RE.test(id) && id !== DONE_COLUMN;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Clean a catalog coming from config or from an HTTP body. Anything unusable is
|
|
51
|
+
* dropped rather than rejected: this reads stored data, and one bad row must not
|
|
52
|
+
* blank out a board.
|
|
53
|
+
*
|
|
54
|
+
* `label: null` means "use the built-in translation for this id" — that is how
|
|
55
|
+
* the four shipped columns stay bilingual while a custom one keeps the exact
|
|
56
|
+
* words its author typed.
|
|
57
|
+
*
|
|
58
|
+
* `on_enter` is the automation: `{ agent, instruction }` means "when a task
|
|
59
|
+
* lands in this column, hand it to that agent". The slug is resolved against
|
|
60
|
+
* the TASK'S OWN project, which is what lets one global "QA" column put each
|
|
61
|
+
* project's own qa agent to work. A project without that agent simply does
|
|
62
|
+
* nothing — a card that silently summoned a stranger would be worse.
|
|
63
|
+
*/
|
|
64
|
+
export function normalizeColumns(value) {
|
|
65
|
+
if (!Array.isArray(value)) return [...DEFAULT_TASK_COLUMNS];
|
|
66
|
+
const seen = new Set();
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const raw of value) {
|
|
69
|
+
const id = String((typeof raw === "string" ? raw : raw?.id) || "").trim().toLowerCase();
|
|
70
|
+
if (!isColumnId(id) || seen.has(id)) continue;
|
|
71
|
+
seen.add(id);
|
|
72
|
+
const label = typeof raw?.label === "string" && raw.label.trim() ? raw.label.trim() : null;
|
|
73
|
+
out.push({ id, label, ...normalizeHook(raw?.on_enter) });
|
|
74
|
+
}
|
|
75
|
+
// A board with no columns is not a board. Falling back beats showing nothing.
|
|
76
|
+
return out.length ? out : [...DEFAULT_TASK_COLUMNS];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The automation configured on a column, or null. */
|
|
80
|
+
export function columnHook(columns, id) {
|
|
81
|
+
return columns.find((c) => c.id === id)?.on_enter || null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The global catalog: every column name that exists anywhere. */
|
|
85
|
+
export function readColumnCatalog(globalConfig) {
|
|
86
|
+
return normalizeColumns(globalConfig?.tasks?.columns);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The columns ONE project shows, in order, with `done` appended.
|
|
91
|
+
*
|
|
92
|
+
* A project that has chosen nothing shows the whole catalog — that is the old
|
|
93
|
+
* behaviour, and a board that starts empty would look broken.
|
|
94
|
+
*
|
|
95
|
+
* @param {object} globalConfig
|
|
96
|
+
* @param {object} projectConfig the project's own `.apc/config.json`
|
|
97
|
+
* @returns {{id: string, label: string|null}[]}
|
|
98
|
+
*/
|
|
99
|
+
export function projectColumns(globalConfig, projectConfig) {
|
|
100
|
+
const catalog = readColumnCatalog(globalConfig);
|
|
101
|
+
const picked = projectConfig?.tasks?.columns;
|
|
102
|
+
if (!Array.isArray(picked) || picked.length === 0) {
|
|
103
|
+
return [...catalog, { id: DONE_COLUMN, label: null }];
|
|
104
|
+
}
|
|
105
|
+
const byId = new Map(catalog.map((c) => [c.id, c]));
|
|
106
|
+
const out = [];
|
|
107
|
+
const seen = new Set();
|
|
108
|
+
for (const raw of picked) {
|
|
109
|
+
const id = String(raw?.id || raw || "").trim().toLowerCase();
|
|
110
|
+
// A project can only pick from the catalog: that is what keeps "QA" the
|
|
111
|
+
// same thing everywhere. An id removed from the catalog quietly disappears
|
|
112
|
+
// from the boards that used it, rather than resurrecting a dead column.
|
|
113
|
+
if (!byId.has(id) || seen.has(id)) continue;
|
|
114
|
+
seen.add(id);
|
|
115
|
+
out.push(byId.get(id));
|
|
116
|
+
}
|
|
117
|
+
if (!out.length) out.push(...catalog);
|
|
118
|
+
return [...out, { id: DONE_COLUMN, label: null }];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Where a task sits on the board.
|
|
123
|
+
*
|
|
124
|
+
* `done` finishes in the done column. `dropped` does NOT: it was abandoned, not
|
|
125
|
+
* completed, and it stays in the column it was abandoned in — which is the
|
|
126
|
+
* useful thing to see when you go looking at what you gave up on and where.
|
|
127
|
+
* Filing it under "done" would claim it was finished.
|
|
128
|
+
*
|
|
129
|
+
* An open task whose status is not a column on THIS board falls into the first
|
|
130
|
+
* one, so a card is never invisible.
|
|
131
|
+
*/
|
|
132
|
+
export function columnFor(task, columns) {
|
|
133
|
+
if (task?.state === "done") return DONE_COLUMN;
|
|
134
|
+
const status = task?.status || DEFAULT_TASK_STATUS;
|
|
135
|
+
return columns.some((c) => c.id === status) ? status : (columns[0]?.id || DEFAULT_TASK_STATUS);
|
|
136
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// Comments on a task that can summon an agent.
|
|
2
|
+
//
|
|
3
|
+
// A comment thread is the one place where "who should do this" and "what is
|
|
4
|
+
// this" sit together, so @-mentioning an agent there is the cheapest way to
|
|
5
|
+
// hand work over: drop a card in QA, tag @qa, and it does the QA and writes
|
|
6
|
+
// back what it found. The reply lands as another comment on the same task.
|
|
7
|
+
//
|
|
8
|
+
// TURN-TAKING IS THE GROUP CHAT'S, ON PURPOSE. `parseMentions` is imported from
|
|
9
|
+
// agent/group/turn-resolver.js rather than copied, and the cascade below is the
|
|
10
|
+
// same shape: an agent's reply is scanned for mentions, whoever is named gets a
|
|
11
|
+
// turn, and a ceiling stops the ping-pong. What is NOT reused is the seeding —
|
|
12
|
+
// a group with no mention hands the turn to the first agent in the room, and a
|
|
13
|
+
// comment with no mention must summon nobody. A note to self is a note to self.
|
|
14
|
+
//
|
|
15
|
+
// The ceiling is lower than the group's ten. A room is watched live by the
|
|
16
|
+
// person who opened it; a task thread runs unattended — that is the entire
|
|
17
|
+
// point of leaving work for QA — so the blast radius of a mention loop is a
|
|
18
|
+
// number nobody is sitting there to stop.
|
|
19
|
+
import { resolveAgentModel } from "#core/agent/agent-model.js";
|
|
20
|
+
import { buildAgentSystem } from "#core/agent/build-agent-system.js";
|
|
21
|
+
import { groupToolIters } from "#core/agent/constants.js";
|
|
22
|
+
import { parseMentions } from "#core/agent/group/turn-resolver.js";
|
|
23
|
+
import { runAgentTurn } from "#core/agent/run-turn.js";
|
|
24
|
+
import { readAgents } from "#core/apc/parser.js";
|
|
25
|
+
import { CHANNELS } from "#core/constants/channels.js";
|
|
26
|
+
import { OWNER_ACTOR_ID } from "#core/constants/actors.js";
|
|
27
|
+
import { emitMessageEvent } from "#core/events/bus.js";
|
|
28
|
+
import { addComment, getTask } from "#core/stores/tasks.js";
|
|
29
|
+
import { nowIso } from "#core/util/time.js";
|
|
30
|
+
|
|
31
|
+
/** One comment can produce at most this many agent replies, cascade included. */
|
|
32
|
+
export const MAX_COMMENT_TURNS = 4;
|
|
33
|
+
|
|
34
|
+
/** Keep a reply readable in a side panel — see the "no huge deploy" rule below. */
|
|
35
|
+
const REPLY_CHAR_HINT = 700;
|
|
36
|
+
|
|
37
|
+
const displayName = (a) => a.fields?.Name || a.slug;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Everyone addressable on a task in this project: the owner (who can be named
|
|
41
|
+
* but never summoned) plus every agent the project declares.
|
|
42
|
+
*/
|
|
43
|
+
export function taskParticipants(projectPath) {
|
|
44
|
+
let agents = [];
|
|
45
|
+
try { agents = readAgents(projectPath); } catch { agents = []; }
|
|
46
|
+
return [
|
|
47
|
+
{ slug: OWNER_ACTOR_ID, name: "Owner", kind: "owner" },
|
|
48
|
+
...agents.map((a) => ({ slug: a.slug, name: displayName(a), kind: "agent" })),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Agent slugs a comment addresses. Exported so the API can resolve them at
|
|
54
|
+
* WRITE time and store them on the comment — the roster can change, and the
|
|
55
|
+
* thread should keep saying who was actually pulled in that day.
|
|
56
|
+
*/
|
|
57
|
+
export function mentionedAgents(text, projectPath, authorSlug = OWNER_ACTOR_ID) {
|
|
58
|
+
return parseMentions(text || "", taskParticipants(projectPath), authorSlug);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The task, rendered for a model that has never seen it. */
|
|
62
|
+
function taskBlock(task, projectName) {
|
|
63
|
+
const lines = [
|
|
64
|
+
"## Task",
|
|
65
|
+
`**${task.title}**`,
|
|
66
|
+
`id: ${task.id} · project: ${projectName} · state: ${task.state} · status: ${task.status}`,
|
|
67
|
+
];
|
|
68
|
+
if (task.due) lines.push(`due: ${task.due}`);
|
|
69
|
+
if (task.agent) lines.push(`assigned: @${task.agent}`);
|
|
70
|
+
if (task.tags?.length) lines.push(`tags: ${task.tags.map((t) => `#${t}`).join(" ")}`);
|
|
71
|
+
if (task.subtask_count) lines.push(`subtasks: ${task.subtask_done}/${task.subtask_count} done`);
|
|
72
|
+
if (task.description) lines.push("", "### Description", task.description);
|
|
73
|
+
// The agent prompt is instructions for whoever RUNS the task, so a summoned
|
|
74
|
+
// agent needs it. It is not the description and must not be read as one.
|
|
75
|
+
if (task.body) lines.push("", "### Agent prompt (instructions for running this task)", task.body);
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The thread so far, oldest first, prefixed by speaker. */
|
|
80
|
+
function threadBlock(task, nameFor) {
|
|
81
|
+
if (!task.comments?.length) return "";
|
|
82
|
+
return [
|
|
83
|
+
"",
|
|
84
|
+
"## Comments",
|
|
85
|
+
...task.comments.map((c) => `${nameFor(c.by)}: ${c.text}`),
|
|
86
|
+
].join("\n");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function systemBlock({ me, others, taskTitle }) {
|
|
90
|
+
const roster = others.length ? others.map((o) => `${o.name} (@${o.slug})`).join(", ") : "(no other agents)";
|
|
91
|
+
return [
|
|
92
|
+
"## Task comments",
|
|
93
|
+
`You were @-mentioned in the comment thread of the task **${taskTitle}**.`,
|
|
94
|
+
`Reply ONLY as yourself (**${me}**). Your reply is posted as a comment on that task — it is not a chat message.`,
|
|
95
|
+
"**Do the work first.** You have your real tools. If the comment asks you to check, review, test or fix something, do it and report what you actually found. Do not describe what you would do.",
|
|
96
|
+
`**Be short.** A comment lives in a side panel next to the task. Aim for a few lines and stay under ~${REPLY_CHAR_HINT} characters. If the detail is long, put the conclusion in the comment and the detail where it belongs (a file, a PR, the task's description).`,
|
|
97
|
+
`**Handing work over:** the only way another agent gets a turn is writing their exact handle with an @ — ${roster}. Writing just their name does NOT reach them. Mention someone ONLY if they genuinely need to act; if you can close it yourself, cite nobody and the thread ends.`,
|
|
98
|
+
].join("\n");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Record an agent→agent handover on the a2a ledger, so a crossing that happened
|
|
103
|
+
* inside a task still shows up where every other agent-to-agent exchange does.
|
|
104
|
+
* The work itself already ran through the real turn engine — this is the paper
|
|
105
|
+
* trail, not the delivery mechanism (an a2a `--deliver` reply has no tools).
|
|
106
|
+
*/
|
|
107
|
+
function logHandover(p, { from, to, taskId, text, model, usage }) {
|
|
108
|
+
const ts = nowIso();
|
|
109
|
+
// Attribution rides along: a row filed without `model`/`usage` reopens in the
|
|
110
|
+
// viewer as "0 tok, no model", which is the regression tests/message-
|
|
111
|
+
// attribution.test.js exists to stop. The numbers are the summoned agent's
|
|
112
|
+
// own turn — the one whose reply this row mirrors.
|
|
113
|
+
const meta = {
|
|
114
|
+
task: taskId, via: "task_comment",
|
|
115
|
+
...(model ? { model } : {}), ...(usage ? { usage } : {}),
|
|
116
|
+
};
|
|
117
|
+
try {
|
|
118
|
+
p.logMessage({
|
|
119
|
+
agent_slug: from, channel: CHANNELS.A2A, direction: "out",
|
|
120
|
+
author: from, body: text, meta: { ...meta, to, final: true }, ts,
|
|
121
|
+
});
|
|
122
|
+
p.logMessage({
|
|
123
|
+
agent_slug: to, channel: CHANNELS.A2A, direction: "in",
|
|
124
|
+
author: from, body: text, meta: { ...meta, from }, ts,
|
|
125
|
+
});
|
|
126
|
+
} catch {
|
|
127
|
+
// The comment is the source of truth; a missing mirror row must never fail
|
|
128
|
+
// the turn that produced it.
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The real thing: one summoned agent's turn, with its own tools. */
|
|
133
|
+
async function runRealTurn({
|
|
134
|
+
p, agent, slug, from, task, participants, nameFor, projectName,
|
|
135
|
+
cfg, projects, plugins, registries, signal,
|
|
136
|
+
}) {
|
|
137
|
+
const modelId = await resolveAgentModel({ agent, config: cfg });
|
|
138
|
+
if (!modelId) throw new Error(`no model for agent ${slug}`);
|
|
139
|
+
const me = displayName(agent);
|
|
140
|
+
const others = participants.filter((x) => x.kind === "agent" && x.slug !== slug);
|
|
141
|
+
const system = buildAgentSystem(p, agent, {
|
|
142
|
+
invocation: "engine",
|
|
143
|
+
caller: from === OWNER_ACTOR_ID ? "user" : from,
|
|
144
|
+
globalConfig: cfg,
|
|
145
|
+
extraParts: [systemBlock({ me, others, taskTitle: task.title })],
|
|
146
|
+
});
|
|
147
|
+
const directive = from === OWNER_ACTOR_ID
|
|
148
|
+
? `The owner tagged you (@${slug}) on this task. Act on the last comment, then reply.`
|
|
149
|
+
: `${nameFor(from)} tagged you (@${slug}) on this task. Act on the last comment, then reply.`;
|
|
150
|
+
|
|
151
|
+
const result = await runAgentTurn({
|
|
152
|
+
p, agent, modelId, system,
|
|
153
|
+
prompt: `${taskBlock(task, projectName)}${threadBlock(task, nameFor)}\n\n---\n${directive}`,
|
|
154
|
+
previousMessages: [],
|
|
155
|
+
channel: CHANNELS.WEB,
|
|
156
|
+
// Same reasoning as the group: the channel picks the PROMPT, but one comment
|
|
157
|
+
// can fan out into several of these runs, so the budget is the fan-out-aware
|
|
158
|
+
// one.
|
|
159
|
+
maxIters: groupToolIters(cfg),
|
|
160
|
+
tools: true,
|
|
161
|
+
projects, plugins, registries, config: cfg,
|
|
162
|
+
signal,
|
|
163
|
+
});
|
|
164
|
+
return { text: result.text || "", model: result.model || modelId, usage: result.usage };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Run the mention cascade for the newest comment on a task.
|
|
169
|
+
*
|
|
170
|
+
* Fire-and-forget from a route: every reply is persisted as it lands, so a
|
|
171
|
+
* caller that never awaits still gets the full thread on its next read.
|
|
172
|
+
*
|
|
173
|
+
* @param {object} args
|
|
174
|
+
* @param {object} args.p resolved project (path, storagePath, config, logMessage, id)
|
|
175
|
+
* @param {string} args.taskId
|
|
176
|
+
* @param {string[]} args.seed agent slugs the triggering comment mentioned
|
|
177
|
+
* @param {string} args.author who wrote the triggering comment (owner | agent slug)
|
|
178
|
+
* @param {AbortSignal} [args.signal]
|
|
179
|
+
* @param {Function} [args.runTurn]
|
|
180
|
+
* Injectable model call, same trick the group resolver uses: the cascade
|
|
181
|
+
* and its ceiling are the part worth testing, and neither needs an
|
|
182
|
+
* engine. Production always uses the real turn.
|
|
183
|
+
* @returns {Promise<Array<{slug, text}>>}
|
|
184
|
+
*/
|
|
185
|
+
export async function runCommentMentions({
|
|
186
|
+
p, taskId, seed, author = OWNER_ACTOR_ID,
|
|
187
|
+
projects, plugins, registries, config, signal = null,
|
|
188
|
+
maxTurns = MAX_COMMENT_TURNS,
|
|
189
|
+
runTurn = runRealTurn,
|
|
190
|
+
}) {
|
|
191
|
+
const participants = taskParticipants(p.path);
|
|
192
|
+
const agents = new Map();
|
|
193
|
+
try {
|
|
194
|
+
for (const a of readAgents(p.path)) agents.set(a.slug, a);
|
|
195
|
+
} catch { /* no roster → nothing to summon */ }
|
|
196
|
+
|
|
197
|
+
const nameFor = (who) =>
|
|
198
|
+
!who || who === OWNER_ACTOR_ID ? "Owner" : (agents.get(who) ? displayName(agents.get(who)) : who);
|
|
199
|
+
|
|
200
|
+
const cfg = p.config || config;
|
|
201
|
+
const projectName = p.name || p.path || String(p.id);
|
|
202
|
+
const said = [];
|
|
203
|
+
const queue = (seed || []).filter((s) => agents.has(s)).map((slug) => ({ slug, from: author }));
|
|
204
|
+
|
|
205
|
+
while (queue.length && said.length < maxTurns) {
|
|
206
|
+
if (signal?.aborted) break;
|
|
207
|
+
const { slug, from } = queue.shift();
|
|
208
|
+
const agent = agents.get(slug);
|
|
209
|
+
if (!agent) continue;
|
|
210
|
+
|
|
211
|
+
// Re-read every hop: an earlier speaker's comment is already persisted, so
|
|
212
|
+
// the next one sees what was just said.
|
|
213
|
+
const task = getTask(p.storagePath, taskId);
|
|
214
|
+
if (!task) break;
|
|
215
|
+
|
|
216
|
+
let reply = "";
|
|
217
|
+
let model = null;
|
|
218
|
+
let usage = null;
|
|
219
|
+
try {
|
|
220
|
+
// A test's stub may hand back a bare string; the real one returns what
|
|
221
|
+
// answered and what it spent.
|
|
222
|
+
const out = await runTurn({
|
|
223
|
+
p, agent, slug, from, task, participants, nameFor, projectName,
|
|
224
|
+
cfg, projects, plugins, registries, signal,
|
|
225
|
+
});
|
|
226
|
+
if (typeof out === "string") reply = out.trim();
|
|
227
|
+
else { reply = (out?.text || "").trim(); model = out?.model || null; usage = out?.usage || null; }
|
|
228
|
+
} catch (e) {
|
|
229
|
+
// A failed summon is written into the thread rather than swallowed: a
|
|
230
|
+
// comment that says "@qa" and is met with silence looks like the feature
|
|
231
|
+
// is broken, which is worse than reading why it did not run.
|
|
232
|
+
reply = `⚠️ No pude atender la mención: ${e?.message || String(e)}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!reply) reply = "…";
|
|
236
|
+
const next = parseMentions(reply, participants, slug);
|
|
237
|
+
addComment(p.storagePath, taskId, { by: slug, text: reply, mentions: next });
|
|
238
|
+
said.push({ slug, text: reply });
|
|
239
|
+
// attribution-exempt: this is the live-feed SIGNAL, not a ledger insertion.
|
|
240
|
+
// It carries "the thread moved" and nothing else — the row it announces was
|
|
241
|
+
// written by addComment above, and the a2a mirror below is where the model
|
|
242
|
+
// and usage are recorded.
|
|
243
|
+
emitMessageEvent({
|
|
244
|
+
scope: "project", channel: CHANNELS.A2A, project_root: p.storagePath,
|
|
245
|
+
agent_slug: slug, direction: "out", type: "agent", thread: taskId, final: true,
|
|
246
|
+
ts: nowIso(),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
for (const to of next) {
|
|
250
|
+
if (!agents.has(to)) continue;
|
|
251
|
+
if (queue.some((q) => q.slug === to)) continue;
|
|
252
|
+
logHandover(p, { from: slug, to, taskId, text: reply, model, usage });
|
|
253
|
+
queue.push({ slug: to, from: slug });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return said;
|
|
258
|
+
}
|
package/src/core/util/time.js
CHANGED
|
@@ -7,3 +7,16 @@ export function nowIso() {
|
|
|
7
7
|
export function isoToMs(iso) {
|
|
8
8
|
return iso ? Date.parse(iso) : 0;
|
|
9
9
|
}
|
|
10
|
+
|
|
11
|
+
// The two shapes the dated note files use (see core/memory/dated-log.js): a
|
|
12
|
+
// "YYYY-MM-DD" day heading and an "HH:MM" bullet tag. UTC on purpose — the
|
|
13
|
+
// notebook, project memory and routine memory have always been stamped in UTC
|
|
14
|
+
// and they are read against each other, so a per-file timezone would make two
|
|
15
|
+
// notes written a minute apart land on different days.
|
|
16
|
+
export function dayStamp(d = new Date()) {
|
|
17
|
+
return d.toISOString().slice(0, 10);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function hourStamp(d = new Date()) {
|
|
21
|
+
return d.toISOString().slice(11, 16);
|
|
22
|
+
}
|
|
@@ -63,7 +63,8 @@ function ttsConfig(globalConfig) {
|
|
|
63
63
|
return globalConfig?.voice?.tts || {};
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
/** The stored config block for one engine id (custom ids included). */
|
|
67
|
+
export function providerConfig(globalConfig, provider) {
|
|
67
68
|
const tts = ttsConfig(globalConfig);
|
|
68
69
|
if (isCustomId(provider)) return tts?.custom?.[slugOf(provider)] || {};
|
|
69
70
|
return tts?.[provider] || {};
|
|
@@ -100,6 +101,58 @@ export function resolveChainOrder(ttsCfg) {
|
|
|
100
101
|
return full;
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Every engine that could handle a synthesize() call, in the order they should
|
|
106
|
+
* be tried. `selectTtsEngine` returns the first of these; `synthesize()` walks
|
|
107
|
+
* the whole list so an engine that *looks* usable but fails at request time
|
|
108
|
+
* (a local server that isn't running) hands off to the next one instead of
|
|
109
|
+
* sinking the turn.
|
|
110
|
+
*
|
|
111
|
+
* WHY THIS EXISTS. isAvailable() is a config probe, not a reachability probe —
|
|
112
|
+
* a custom endpoint reports itself available on the strength of having a
|
|
113
|
+
* base_url. That is the right call for the settings UI (it must list an engine
|
|
114
|
+
* the user configured even while its server is down) but it made "chain" mode
|
|
115
|
+
* a misnomer: selection stopped at the first *configured* engine and a failure
|
|
116
|
+
* there never reached the second. Falling back on the real error is both more
|
|
117
|
+
* accurate than any probe and free.
|
|
118
|
+
*
|
|
119
|
+
* @returns {Promise<Array<{provider, adapter, engineConfig}>>}
|
|
120
|
+
*/
|
|
121
|
+
export async function resolveTtsCandidates({ globalConfig, provider }) {
|
|
122
|
+
const ttsCfg = ttsConfig(globalConfig);
|
|
123
|
+
const entry = (id) => ({
|
|
124
|
+
provider: id,
|
|
125
|
+
adapter: getTtsAdapter(id),
|
|
126
|
+
engineConfig: providerConfig(globalConfig, id),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// An explicit provider means the caller wants THAT engine — the voice
|
|
130
|
+
// tester, or a retry. No fallback: silently speaking in a different voice
|
|
131
|
+
// than the one under test would be worse than failing.
|
|
132
|
+
if (provider && provider !== "auto") return [entry(provider)];
|
|
133
|
+
|
|
134
|
+
if (resolveMode(ttsCfg) === "single") {
|
|
135
|
+
const id = ttsCfg?.provider;
|
|
136
|
+
if (id && id !== "auto") return [entry(id)];
|
|
137
|
+
// Misconfigured single mode (no concrete provider) → fall through to chain.
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const out = [];
|
|
141
|
+
for (const id of resolveChainOrder(ttsCfg)) {
|
|
142
|
+
if (id !== "mock" && !isEnabled(ttsCfg, id)) continue;
|
|
143
|
+
const cfg = providerConfig(globalConfig, id);
|
|
144
|
+
try {
|
|
145
|
+
if (id !== "mock" && !(await getTtsAdapter(id).isAvailable(cfg, globalConfig?.engines))) continue;
|
|
146
|
+
} catch { continue; }
|
|
147
|
+
out.push(entry(id));
|
|
148
|
+
}
|
|
149
|
+
// mock closes the list so synthesize() never throws for want of an engine.
|
|
150
|
+
// It returns silence, so callers that can tell the user "no voice" should
|
|
151
|
+
// check the returned `provider` rather than treat it as speech.
|
|
152
|
+
if (!out.some((c) => c.provider === "mock")) out.push(entry("mock"));
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
103
156
|
/**
|
|
104
157
|
* Resolve which engine should handle a synthesize() call.
|
|
105
158
|
* Returns { provider, adapter, engineConfig }.
|
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
// OpenAI-compatible speech server (e.g. a Qwen3-TTS / QVox daemon at
|
|
9
9
|
// http://127.0.0.1:5111/v1). When base_url is set we additionally forward the
|
|
10
10
|
// non-OpenAI fields that server understands — `instruct` (the base voice, from
|
|
11
|
-
// the `style` arg), `language` and `temperature`. These
|
|
12
|
-
// to stock OpenAI (only when base_url is present), so the
|
|
13
|
-
// byte-for-byte compatible.
|
|
11
|
+
// the `style` arg), `language`, `clone`/`ref_text` and `temperature`. These
|
|
12
|
+
// extras are NEVER sent to stock OpenAI (only when base_url is present), so the
|
|
13
|
+
// standard path stays byte-for-byte compatible.
|
|
14
|
+
//
|
|
15
|
+
// Which of `clone` / `voice` / `instruct` is set decides HOW the server speaks,
|
|
16
|
+
// and they are not additive — QVox reads them in that order and the first one
|
|
17
|
+
// wins. A named speaker carries its own accent, so picking one leaves `style`
|
|
18
|
+
// describing only the delivery; only a cloned reference can carry an accent no
|
|
19
|
+
// preset speaker has.
|
|
14
20
|
|
|
15
21
|
import fs from "node:fs";
|
|
16
22
|
import path from "node:path";
|
|
@@ -59,6 +65,34 @@ export default {
|
|
|
59
65
|
return Boolean(config.base_url) || Boolean(getKey(config, parentEnginesCfg));
|
|
60
66
|
},
|
|
61
67
|
|
|
68
|
+
// Only local endpoints have anything to warm. A cloud API is as ready as it
|
|
69
|
+
// will ever be, and a hosted server is not ours to spin up — so a stock
|
|
70
|
+
// OpenAI config skips instead of sending a pointless request.
|
|
71
|
+
async warmup(config = {}) {
|
|
72
|
+
if (!config.base_url) return { ok: true, skipped: "not a local endpoint" };
|
|
73
|
+
const url = config.base_url.replace(/\/+$/, "") + "/warmup";
|
|
74
|
+
const res = await fetch(url, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: {
|
|
77
|
+
"content-type": "application/json",
|
|
78
|
+
...(config.api_key ? { authorization: `Bearer ${config.api_key}` } : {}),
|
|
79
|
+
},
|
|
80
|
+
// Say which voice, so the endpoint warms the model that will answer
|
|
81
|
+
// rather than a sibling of it — these are different multi-gigabyte
|
|
82
|
+
// checkpoints, and warming one leaves the other exactly as cold. The
|
|
83
|
+
// clone is named for the same reason: a cloned reference is served by a
|
|
84
|
+
// third checkpoint again, so a warmup that only ever mentions `voice`
|
|
85
|
+
// warms nothing the next request will use.
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
...(config.voice ? { voice: config.voice } : {}),
|
|
88
|
+
...(config.clone ? { clone: config.clone, ref_text: config.ref_text } : {}),
|
|
89
|
+
}),
|
|
90
|
+
signal: AbortSignal.timeout(120_000),
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) throw new Error(`warmup ${res.status}`);
|
|
93
|
+
return await res.json().catch(() => ({ ok: true }));
|
|
94
|
+
},
|
|
95
|
+
|
|
62
96
|
async synthesize({ text, voice, language, style, outDir, config = {}, format, signal, parentEnginesCfg }) {
|
|
63
97
|
if (!text) throw new Error("openai-tts: empty text");
|
|
64
98
|
const isCustom = Boolean(config.base_url);
|
|
@@ -79,7 +113,19 @@ export default {
|
|
|
79
113
|
if (isCustom) {
|
|
80
114
|
// QVox / Qwen3-TTS extras (ignored by stock OpenAI, so only sent here).
|
|
81
115
|
if (styleHint) body.instruct = styleHint;
|
|
82
|
-
|
|
116
|
+
// A caller that names a language wins; otherwise the configured one.
|
|
117
|
+
// Nothing in the speaking path (Telegram voice notes, the desktop, a
|
|
118
|
+
// routine) passes one, so without this the server was left to guess from
|
|
119
|
+
// its own default — right by luck here, wrong for anyone whose local
|
|
120
|
+
// server defaults elsewhere.
|
|
121
|
+
const lang = language || config.language;
|
|
122
|
+
if (lang && lang !== "auto") body.language = lang;
|
|
123
|
+
// Voice cloning: the endpoint reads the reference itself, so this is a
|
|
124
|
+
// path on ITS filesystem, not ours. `ref_text` is what the recording
|
|
125
|
+
// says — optional, and worth setting: telling the model that measured
|
|
126
|
+
// 15.8 chars/s against 12.9 without it.
|
|
127
|
+
if (config.clone) body.clone = config.clone;
|
|
128
|
+
if (config.ref_text) body.ref_text = config.ref_text;
|
|
83
129
|
if (config.temperature != null) body.temperature = config.temperature;
|
|
84
130
|
} else if (styleHint && /gpt-4o.*tts/i.test(model || "")) {
|
|
85
131
|
// Stock OpenAI's newer TTS models accept a natural-language `instructions`.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Re-encode synthesized speech into the one container Telegram will render as
|
|
2
|
+
// a VOICE NOTE.
|
|
3
|
+
//
|
|
4
|
+
// This is not a cosmetic detail. `sendVoice` accepts OGG/Opus and nothing
|
|
5
|
+
// else; hand it the WAV that Gemini TTS returns (or the MP3 from OpenAI /
|
|
6
|
+
// ElevenLabs) and the Bot API either rejects the upload or Telegram shows a
|
|
7
|
+
// grey file attachment — which on a phone mounted in a car is unplayable
|
|
8
|
+
// without taking your hand off the wheel. So: one conversion, in one place,
|
|
9
|
+
// and callers that need a voice note ask for one instead of guessing at
|
|
10
|
+
// formats.
|
|
11
|
+
//
|
|
12
|
+
// ffmpeg is the same dependency the transcription path already relies on, and
|
|
13
|
+
// it is reached through envWithPath() for the same reason: a daemon booted by
|
|
14
|
+
// launchd inherits a PATH with no Homebrew in it, and every conversion would
|
|
15
|
+
// otherwise fail with ENOENT (see core/util/path-env.js).
|
|
16
|
+
import fs from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { execFile } from "node:child_process";
|
|
19
|
+
import { promisify } from "node:util";
|
|
20
|
+
import { envWithPath } from "#core/util/path-env.js";
|
|
21
|
+
import { ensureTtsTmpDir } from "./tts.js";
|
|
22
|
+
|
|
23
|
+
const run = promisify(execFile);
|
|
24
|
+
|
|
25
|
+
// Telegram voice notes are mono and low-bitrate by convention; Opus at 32 kbps
|
|
26
|
+
// is transparent for speech and keeps a 20-second note around 80 KB.
|
|
27
|
+
const VOICE_BITRATE = "32k";
|
|
28
|
+
const VOICE_SAMPLE_RATE = "48000";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Convert `sourcePath` to OGG/Opus and return the new path. A file that is
|
|
32
|
+
* already .ogg is returned untouched — every engine that emits Opus natively
|
|
33
|
+
* (ElevenLabs' ogg formats, Gemini when it answers audio/ogg) skips the
|
|
34
|
+
* re-encode entirely.
|
|
35
|
+
*
|
|
36
|
+
* Returns `null` when ffmpeg is unavailable or fails, so callers can fall back
|
|
37
|
+
* to plain text rather than dropping the reply. A voice note is an upgrade;
|
|
38
|
+
* losing the message because the upgrade failed is not acceptable.
|
|
39
|
+
*/
|
|
40
|
+
export async function toVoiceNote(sourcePath) {
|
|
41
|
+
if (!sourcePath || !fs.existsSync(sourcePath)) return null;
|
|
42
|
+
if (path.extname(sourcePath).toLowerCase() === ".ogg") return sourcePath;
|
|
43
|
+
|
|
44
|
+
const target = path.join(
|
|
45
|
+
ensureTtsTmpDir(),
|
|
46
|
+
`${path.basename(sourcePath, path.extname(sourcePath))}.ogg`
|
|
47
|
+
);
|
|
48
|
+
try {
|
|
49
|
+
await run(
|
|
50
|
+
"ffmpeg",
|
|
51
|
+
[
|
|
52
|
+
"-hide_banner", "-loglevel", "error", "-y",
|
|
53
|
+
"-i", sourcePath,
|
|
54
|
+
"-vn",
|
|
55
|
+
"-ac", "1",
|
|
56
|
+
"-ar", VOICE_SAMPLE_RATE,
|
|
57
|
+
"-c:a", "libopus",
|
|
58
|
+
"-b:a", VOICE_BITRATE,
|
|
59
|
+
target,
|
|
60
|
+
],
|
|
61
|
+
{ env: envWithPath(), timeout: 60_000 }
|
|
62
|
+
);
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return fs.existsSync(target) && fs.statSync(target).size > 0 ? target : null;
|
|
67
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Text that is about to be spoken aloud, cleaned of what a voice cannot say.
|
|
2
|
+
//
|
|
3
|
+
// A TTS model has to produce *something* for every token it is given, and an
|
|
4
|
+
// emoji has no pronunciation — so it improvises, which comes out as a couple of
|
|
5
|
+
// seconds of humming in the middle of a sentence. Roby ending a reply with 👍
|
|
6
|
+
// is pleasant to read and unlistenable.
|
|
7
|
+
//
|
|
8
|
+
// This is deliberately about speech, not about display: a channel that shows
|
|
9
|
+
// the text as well decides separately whether the reader keeps the emoji.
|
|
10
|
+
|
|
11
|
+
// Pictographs, plus the pieces that glue them together: variation selectors,
|
|
12
|
+
// zero-width joiners, skin-tone modifiers, keycap marks and the regional
|
|
13
|
+
// indicators that spell out flags.
|
|
14
|
+
// Written as alternatives rather than one character class on purpose: the
|
|
15
|
+
// joiners below are combining marks, and a class that mixes them with the
|
|
16
|
+
// pictographs they attach to is both misleading to read and flagged by lint.
|
|
17
|
+
const EMOJI = new RegExp(
|
|
18
|
+
[
|
|
19
|
+
"\\p{Extended_Pictographic}", // the pictographs themselves
|
|
20
|
+
"\\p{Regional_Indicator}", // the letter pairs that spell out flags
|
|
21
|
+
"[\\u{1F3FB}-\\u{1F3FF}]", // skin-tone modifiers
|
|
22
|
+
"\\uFE0F", // variation selector (the "render as emoji" flag)
|
|
23
|
+
"\\u200D", // zero-width joiner, glues 👨👩👧 together
|
|
24
|
+
"\\u20E3", // the enclosing box of a keycap like 1️⃣
|
|
25
|
+
].join("|"),
|
|
26
|
+
"gu"
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Remove emoji from text meant to be spoken, and tidy the space they leave.
|
|
31
|
+
* Returns "" when nothing sayable is left, which lets a caller drop a segment
|
|
32
|
+
* that was only an emoji instead of synthesizing a shrug.
|
|
33
|
+
*/
|
|
34
|
+
export function stripEmoji(text) {
|
|
35
|
+
return String(text ?? "")
|
|
36
|
+
.replace(EMOJI, "")
|
|
37
|
+
// An emoji sitting between words leaves a double space; one that trailed a
|
|
38
|
+
// sentence leaves a space before the period.
|
|
39
|
+
.replace(/[ \t]{2,}/g, " ")
|
|
40
|
+
.replace(/[ \t]+([,.;:!?…])/g, "$1")
|
|
41
|
+
.replace(/^[ \t]+|[ \t]+$/gm, "")
|
|
42
|
+
.trim();
|
|
43
|
+
}
|