@rubytech/taskmaster 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/auth-profiles/oauth.js +24 -0
- package/dist/agents/auth-profiles/profiles.js +37 -0
- package/dist/agents/auth-profiles.js +1 -1
- package/dist/agents/pi-tools.policy.js +4 -0
- package/dist/agents/taskmaster-tools.js +14 -0
- package/dist/agents/tool-policy.js +5 -2
- package/dist/agents/tools/apikeys-tool.js +16 -5
- package/dist/agents/tools/contact-create-tool.js +59 -0
- package/dist/agents/tools/contact-delete-tool.js +48 -0
- package/dist/agents/tools/contact-update-tool.js +17 -2
- package/dist/agents/tools/file-delete-tool.js +137 -0
- package/dist/agents/tools/file-list-tool.js +127 -0
- package/dist/agents/tools/message-history-tool.js +2 -3
- package/dist/auto-reply/media-note.js +11 -0
- package/dist/auto-reply/reply/commands-tts.js +7 -2
- package/dist/auto-reply/reply/get-reply.js +4 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/provision-seed.js +1 -2
- package/dist/commands/doctor-config-flow.js +13 -0
- package/dist/config/agent-tools-reconcile.js +53 -0
- package/dist/config/defaults.js +10 -1
- package/dist/config/legacy.migrations.part-3.js +26 -0
- package/dist/config/zod-schema.core.js +9 -1
- package/dist/config/zod-schema.js +1 -0
- package/dist/control-ui/assets/{index-N8du4fwV.js → index-BDETQp97.js} +692 -600
- package/dist/control-ui/assets/index-BDETQp97.js.map +1 -0
- package/dist/control-ui/assets/index-CPawOl_z.css +1 -0
- package/dist/control-ui/index.html +2 -2
- package/dist/gateway/chat-sanitize.js +5 -1
- package/dist/gateway/config-reload.js +1 -0
- package/dist/gateway/media-http.js +28 -0
- package/dist/gateway/server/tls.js +2 -2
- package/dist/gateway/server-http.js +34 -4
- package/dist/gateway/server-methods/apikeys.js +56 -4
- package/dist/gateway/server-methods/chat.js +64 -25
- package/dist/gateway/server-methods/tts.js +11 -2
- package/dist/gateway/server.impl.js +38 -5
- package/dist/infra/tls/gateway.js +19 -3
- package/dist/media-understanding/apply.js +35 -0
- package/dist/media-understanding/providers/deepgram/audio.js +1 -1
- package/dist/media-understanding/providers/google/audio.js +1 -1
- package/dist/media-understanding/providers/google/video.js +1 -1
- package/dist/media-understanding/providers/index.js +2 -0
- package/dist/media-understanding/providers/openai/audio.js +1 -1
- package/dist/media-understanding/providers/sherpa-onnx/index.js +10 -0
- package/dist/media-understanding/runner.js +61 -72
- package/dist/media-understanding/sherpa-onnx-local.js +223 -0
- package/dist/memory/audit.js +9 -0
- package/dist/memory/manager.js +1 -1
- package/dist/records/records-manager.js +10 -0
- package/dist/tts/tts.js +98 -10
- package/dist/web/auto-reply/monitor/process-message.js +45 -17
- package/dist/web/inbound/monitor.js +9 -1
- package/extensions/diagnostics-otel/node_modules/.bin/acorn +0 -0
- package/extensions/googlechat/node_modules/.bin/taskmaster +2 -2
- package/extensions/googlechat/package.json +2 -2
- package/extensions/line/node_modules/.bin/taskmaster +2 -2
- package/extensions/line/package.json +1 -1
- package/extensions/matrix/node_modules/.bin/markdown-it +0 -0
- package/extensions/matrix/node_modules/.bin/taskmaster +2 -2
- package/extensions/matrix/package.json +1 -1
- package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +0 -0
- package/extensions/memory-lancedb/node_modules/.bin/openai +0 -0
- package/extensions/msteams/node_modules/.bin/taskmaster +2 -2
- package/extensions/msteams/package.json +1 -1
- package/extensions/nostr/node_modules/.bin/taskmaster +2 -2
- package/extensions/nostr/node_modules/.bin/tsc +0 -0
- package/extensions/nostr/node_modules/.bin/tsserver +0 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/zalo/node_modules/.bin/taskmaster +2 -2
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/node_modules/.bin/taskmaster +2 -2
- package/extensions/zalouser/package.json +1 -1
- package/package.json +56 -65
- package/scripts/install.sh +0 -0
- package/scripts/postinstall.js +76 -0
- package/skills/business-assistant/references/crm.md +32 -8
- package/taskmaster-docs/USER-GUIDE.md +111 -6
- package/templates/.DS_Store +0 -0
- package/templates/beagle/agents/admin/AGENTS.md +4 -2
- package/templates/customer/.DS_Store +0 -0
- package/templates/customer/agents/.DS_Store +0 -0
- package/templates/maxy/.DS_Store +0 -0
- package/templates/maxy/.gitignore +1 -0
- package/templates/maxy/agents/.DS_Store +0 -0
- package/templates/maxy/agents/admin/.DS_Store +0 -0
- package/templates/maxy/memory/.DS_Store +0 -0
- package/templates/maxy/skills/.DS_Store +0 -0
- package/templates/taskmaster/.gitignore +1 -0
- package/templates/taskmaster/agents/admin/AGENTS.md +1 -0
- package/dist/control-ui/assets/index-DtQHRIVD.css +0 -1
- package/dist/control-ui/assets/index-N8du4fwV.js.map +0 -1
package/dist/tts/tts.js
CHANGED
|
@@ -5,11 +5,13 @@ import { completeSimple } from "@mariozechner/pi-ai";
|
|
|
5
5
|
import { EdgeTTS } from "node-edge-tts";
|
|
6
6
|
import { normalizeChannelId } from "../channels/plugins/index.js";
|
|
7
7
|
import { logVerbose } from "../globals.js";
|
|
8
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
8
9
|
import { isVoiceCompatibleAudio } from "../media/audio.js";
|
|
9
10
|
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
|
10
|
-
import { getApiKeyForModel, requireApiKey } from "../agents/model-auth.js";
|
|
11
|
+
import { getApiKeyForModel, requireApiKey, resolveApiKeyForProvider, } from "../agents/model-auth.js";
|
|
11
12
|
import { buildModelAliasIndex, resolveDefaultModelForAgent, resolveModelRefFromString, } from "../agents/model-selection.js";
|
|
12
13
|
import { resolveModel } from "../agents/pi-embedded-runner/model.js";
|
|
14
|
+
const log = createSubsystemLogger("gateway/tts");
|
|
13
15
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
14
16
|
const DEFAULT_TTS_MAX_LENGTH = 1500;
|
|
15
17
|
const DEFAULT_TTS_SUMMARIZE = true;
|
|
@@ -133,6 +135,11 @@ export function resolveTtsConfig(cfg) {
|
|
|
133
135
|
proxy: raw.edge?.proxy?.trim() || undefined,
|
|
134
136
|
timeoutMs: raw.edge?.timeoutMs,
|
|
135
137
|
},
|
|
138
|
+
hume: {
|
|
139
|
+
apiKey: raw.hume?.apiKey,
|
|
140
|
+
voice: raw.hume?.voice?.trim() || "KORA",
|
|
141
|
+
description: raw.hume?.description?.trim() || undefined,
|
|
142
|
+
},
|
|
136
143
|
prefsPath: raw.prefsPath,
|
|
137
144
|
maxTextLength: raw.maxTextLength ?? DEFAULT_MAX_TEXT_LENGTH,
|
|
138
145
|
timeoutMs: raw.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
@@ -173,15 +180,15 @@ export function buildTtsSystemPromptHint(cfg) {
|
|
|
173
180
|
const maxLength = getTtsMaxLength(prefsPath);
|
|
174
181
|
const summarize = isSummarizationEnabled(prefsPath) ? "on" : "off";
|
|
175
182
|
const autoHint = autoMode === "inbound"
|
|
176
|
-
? "
|
|
183
|
+
? "Your replies are automatically converted to voice notes when the user sends audio/voice."
|
|
177
184
|
: autoMode === "tagged"
|
|
178
|
-
? "
|
|
179
|
-
:
|
|
185
|
+
? "Your replies are converted to voice notes when you include [[tts]] or [[tts:text]] tags."
|
|
186
|
+
: "Your replies are automatically converted to voice notes and sent as audio messages.";
|
|
180
187
|
return [
|
|
181
|
-
"Voice
|
|
188
|
+
"Voice replies are enabled — you CAN send voice notes.",
|
|
182
189
|
autoHint,
|
|
190
|
+
"Write naturally as if speaking. Do not tell the user you cannot send voice messages — you can.",
|
|
183
191
|
`Keep spoken text ≤${maxLength} chars to avoid auto-summary (summary ${summarize}).`,
|
|
184
|
-
"Use [[tts:...]] and optional [[tts:text]]...[[/tts:text]] to control voice/expressiveness.",
|
|
185
192
|
]
|
|
186
193
|
.filter(Boolean)
|
|
187
194
|
.join("\n");
|
|
@@ -291,15 +298,20 @@ export function resolveTtsApiKey(config, provider) {
|
|
|
291
298
|
if (provider === "openai") {
|
|
292
299
|
return config.openai.apiKey || process.env.OPENAI_API_KEY;
|
|
293
300
|
}
|
|
301
|
+
if (provider === "hume") {
|
|
302
|
+
return config.hume.apiKey || process.env.HUME_API_KEY;
|
|
303
|
+
}
|
|
294
304
|
return undefined;
|
|
295
305
|
}
|
|
296
|
-
export const TTS_PROVIDERS = ["openai", "elevenlabs", "edge"];
|
|
306
|
+
export const TTS_PROVIDERS = ["openai", "elevenlabs", "hume", "edge"];
|
|
297
307
|
export function resolveTtsProviderOrder(primary) {
|
|
298
308
|
return [primary, ...TTS_PROVIDERS.filter((provider) => provider !== primary)];
|
|
299
309
|
}
|
|
300
310
|
export function isTtsProviderConfigured(config, provider) {
|
|
301
311
|
if (provider === "edge")
|
|
302
312
|
return config.edge.enabled;
|
|
313
|
+
if (provider === "hume")
|
|
314
|
+
return Boolean(resolveTtsApiKey(config, provider));
|
|
303
315
|
return Boolean(resolveTtsApiKey(config, provider));
|
|
304
316
|
}
|
|
305
317
|
function isValidVoiceId(voiceId) {
|
|
@@ -350,6 +362,14 @@ function normalizeSeed(seed) {
|
|
|
350
362
|
}
|
|
351
363
|
return next;
|
|
352
364
|
}
|
|
365
|
+
/** Strip emoji characters so TTS engines don't read them aloud by name. */
|
|
366
|
+
function stripEmoji(text) {
|
|
367
|
+
// Unicode emoji ranges: emoticons, dingbats, symbols, flags, components
|
|
368
|
+
return text
|
|
369
|
+
.replace(/[\p{Emoji_Presentation}\p{Extended_Pictographic}]/gu, "")
|
|
370
|
+
.replace(/ +/g, " ")
|
|
371
|
+
.trim();
|
|
372
|
+
}
|
|
353
373
|
function parseBooleanValue(value) {
|
|
354
374
|
const normalized = value.trim().toLowerCase();
|
|
355
375
|
if (["true", "1", "yes", "on"].includes(normalized))
|
|
@@ -777,6 +797,50 @@ async function openaiTTS(params) {
|
|
|
777
797
|
clearTimeout(timeout);
|
|
778
798
|
}
|
|
779
799
|
}
|
|
800
|
+
// ---------------------------------------------------------------------------
|
|
801
|
+
// Hume Octave TTS — REST API
|
|
802
|
+
// POST https://api.hume.ai/v0/tts
|
|
803
|
+
// ---------------------------------------------------------------------------
|
|
804
|
+
const HUME_TTS_BASE_URL = "https://api.hume.ai/v0/tts";
|
|
805
|
+
async function humeTTS(params) {
|
|
806
|
+
const { text, apiKey, voice, description, timeoutMs } = params;
|
|
807
|
+
const controller = new AbortController();
|
|
808
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
809
|
+
try {
|
|
810
|
+
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(voice);
|
|
811
|
+
const voiceSpec = isUuid ? { id: voice } : { name: voice, provider: "HUME_AI" };
|
|
812
|
+
const utterance = { text, voice: voiceSpec };
|
|
813
|
+
if (description) {
|
|
814
|
+
utterance.description = description;
|
|
815
|
+
}
|
|
816
|
+
const response = await fetch(HUME_TTS_BASE_URL, {
|
|
817
|
+
method: "POST",
|
|
818
|
+
headers: {
|
|
819
|
+
"X-Hume-Api-Key": apiKey,
|
|
820
|
+
"Content-Type": "application/json",
|
|
821
|
+
},
|
|
822
|
+
body: JSON.stringify({
|
|
823
|
+
utterances: [utterance],
|
|
824
|
+
format: { type: "mp3" },
|
|
825
|
+
strip_headers: true,
|
|
826
|
+
}),
|
|
827
|
+
signal: controller.signal,
|
|
828
|
+
});
|
|
829
|
+
if (!response.ok) {
|
|
830
|
+
const body = await response.text().catch(() => "");
|
|
831
|
+
throw new Error(`Hume TTS API error (${response.status}): ${body.slice(0, 200)}`);
|
|
832
|
+
}
|
|
833
|
+
const json = (await response.json());
|
|
834
|
+
const audio64 = json.generations?.[0]?.audio;
|
|
835
|
+
if (!audio64) {
|
|
836
|
+
throw new Error("Hume TTS: no audio in response");
|
|
837
|
+
}
|
|
838
|
+
return Buffer.from(audio64, "base64");
|
|
839
|
+
}
|
|
840
|
+
finally {
|
|
841
|
+
clearTimeout(timeout);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
780
844
|
function inferEdgeExtension(outputFormat) {
|
|
781
845
|
const normalized = outputFormat.toLowerCase();
|
|
782
846
|
if (normalized.includes("webm"))
|
|
@@ -888,7 +952,17 @@ export async function textToSpeech(params) {
|
|
|
888
952
|
voiceCompatible,
|
|
889
953
|
};
|
|
890
954
|
}
|
|
891
|
-
|
|
955
|
+
let apiKey = resolveTtsApiKey(config, provider);
|
|
956
|
+
// Hume keys are stored in auth profiles (API Keys UI). Fall back to async lookup.
|
|
957
|
+
if (!apiKey && provider === "hume") {
|
|
958
|
+
try {
|
|
959
|
+
const auth = await resolveApiKeyForProvider({ provider: "hume", cfg: params.cfg });
|
|
960
|
+
apiKey = auth.apiKey?.trim() || undefined;
|
|
961
|
+
}
|
|
962
|
+
catch {
|
|
963
|
+
// No auth profile key available
|
|
964
|
+
}
|
|
965
|
+
}
|
|
892
966
|
if (!apiKey) {
|
|
893
967
|
lastError = `No API key for ${provider}`;
|
|
894
968
|
continue;
|
|
@@ -918,6 +992,15 @@ export async function textToSpeech(params) {
|
|
|
918
992
|
timeoutMs: config.timeoutMs,
|
|
919
993
|
});
|
|
920
994
|
}
|
|
995
|
+
else if (provider === "hume") {
|
|
996
|
+
audioBuffer = await humeTTS({
|
|
997
|
+
text: params.text,
|
|
998
|
+
apiKey,
|
|
999
|
+
voice: config.hume.voice,
|
|
1000
|
+
description: config.hume.description,
|
|
1001
|
+
timeoutMs: config.timeoutMs,
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
921
1004
|
else {
|
|
922
1005
|
const openaiModelOverride = params.overrides?.openai?.model;
|
|
923
1006
|
const openaiVoiceOverride = params.overrides?.openai?.voice;
|
|
@@ -931,6 +1014,7 @@ export async function textToSpeech(params) {
|
|
|
931
1014
|
});
|
|
932
1015
|
}
|
|
933
1016
|
const latencyMs = Date.now() - providerStart;
|
|
1017
|
+
log.info(`success via ${provider} (${latencyMs}ms, ${audioBuffer.length} bytes)`);
|
|
934
1018
|
const tempDir = mkdtempSync(path.join(tmpdir(), "tts-"));
|
|
935
1019
|
const audioPath = path.join(tempDir, `voice-${Date.now()}${output.extension}`);
|
|
936
1020
|
writeFileSync(audioPath, audioBuffer);
|
|
@@ -940,7 +1024,7 @@ export async function textToSpeech(params) {
|
|
|
940
1024
|
audioPath,
|
|
941
1025
|
latencyMs,
|
|
942
1026
|
provider,
|
|
943
|
-
outputFormat: provider === "openai" ? output.openai : output.elevenlabs,
|
|
1027
|
+
outputFormat: provider === "openai" ? output.openai : provider === "hume" ? "mp3" : output.elevenlabs,
|
|
944
1028
|
voiceCompatible: output.voiceCompatible,
|
|
945
1029
|
};
|
|
946
1030
|
}
|
|
@@ -952,8 +1036,10 @@ export async function textToSpeech(params) {
|
|
|
952
1036
|
else {
|
|
953
1037
|
lastError = `${provider}: ${error.message}`;
|
|
954
1038
|
}
|
|
1039
|
+
log.error(`${provider} failed: ${lastError}`);
|
|
955
1040
|
}
|
|
956
1041
|
}
|
|
1042
|
+
log.error(`all providers failed: ${lastError || "none available"}`);
|
|
957
1043
|
return {
|
|
958
1044
|
success: false,
|
|
959
1045
|
error: `TTS conversion failed: ${lastError || "no providers available"}`,
|
|
@@ -1033,8 +1119,10 @@ export async function textToSpeechTelephony(params) {
|
|
|
1033
1119
|
else {
|
|
1034
1120
|
lastError = `${provider}: ${error.message}`;
|
|
1035
1121
|
}
|
|
1122
|
+
log.error(`telephony: ${provider} failed: ${lastError}`);
|
|
1036
1123
|
}
|
|
1037
1124
|
}
|
|
1125
|
+
log.error(`telephony: all providers failed: ${lastError || "none available"}`);
|
|
1038
1126
|
return {
|
|
1039
1127
|
success: false,
|
|
1040
1128
|
error: `TTS conversion failed: ${lastError || "no providers available"}`,
|
|
@@ -1081,7 +1169,7 @@ export async function maybeApplyTtsToPayload(params) {
|
|
|
1081
1169
|
if (ttsText.trim().length < 10)
|
|
1082
1170
|
return nextPayload;
|
|
1083
1171
|
const maxLength = getTtsMaxLength(prefsPath);
|
|
1084
|
-
let textForAudio = ttsText.trim();
|
|
1172
|
+
let textForAudio = stripEmoji(ttsText.trim());
|
|
1085
1173
|
let wasSummarized = false;
|
|
1086
1174
|
if (textForAudio.length > maxLength) {
|
|
1087
1175
|
if (!isSummarizationEnabled(prefsPath)) {
|
|
@@ -130,23 +130,32 @@ export async function processMessage(params) {
|
|
|
130
130
|
params.echoForget(combinedEchoKey);
|
|
131
131
|
return false;
|
|
132
132
|
}
|
|
133
|
-
// Fire message:inbound hook for conversation archiving
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
133
|
+
// Fire message:inbound hook for conversation archiving.
|
|
134
|
+
// For media messages (audio, image, video) the raw body is a placeholder like
|
|
135
|
+
// `<media:audio>`. Defer the hook until after media understanding resolves so
|
|
136
|
+
// the archive records the transcript / description instead of the dead link.
|
|
137
|
+
// Text-only messages fire immediately (unchanged behaviour).
|
|
138
|
+
const hasMediaAttachment = Boolean(params.msg.mediaType);
|
|
139
|
+
const fireInboundArchiveHook = (text) => {
|
|
140
|
+
void triggerInternalHook(createInternalHookEvent("message", "inbound", params.route.sessionKey, {
|
|
141
|
+
from: params.msg.senderE164 ?? params.msg.from,
|
|
142
|
+
to: params.msg.to,
|
|
143
|
+
text,
|
|
144
|
+
timestamp: params.msg.timestamp ?? Date.now(),
|
|
145
|
+
chatType: params.msg.chatType,
|
|
146
|
+
agentId: params.route.agentId,
|
|
147
|
+
channel: "whatsapp",
|
|
148
|
+
mediaType: params.msg.mediaType,
|
|
149
|
+
senderName: params.msg.senderName,
|
|
150
|
+
senderE164: params.msg.senderE164,
|
|
151
|
+
groupSubject: params.msg.groupSubject,
|
|
152
|
+
conversationId,
|
|
153
|
+
cfg: params.cfg,
|
|
154
|
+
}));
|
|
155
|
+
};
|
|
156
|
+
if (!hasMediaAttachment) {
|
|
157
|
+
fireInboundArchiveHook(params.msg.body);
|
|
158
|
+
}
|
|
150
159
|
// Send ack reaction immediately upon message receipt (post-gating).
|
|
151
160
|
// Suppress when running silently on un-mentioned group messages.
|
|
152
161
|
if (params.suppressDelivery) {
|
|
@@ -233,6 +242,7 @@ export async function processMessage(params) {
|
|
|
233
242
|
MediaPath: params.msg.mediaPath,
|
|
234
243
|
MediaUrl: params.msg.mediaUrl,
|
|
235
244
|
MediaType: params.msg.mediaType,
|
|
245
|
+
MediaDownloadFailed: params.msg.mediaDownloadFailed,
|
|
236
246
|
ChatType: params.msg.chatType,
|
|
237
247
|
ConversationLabel: params.msg.chatType === "group" ? conversationId : params.msg.from,
|
|
238
248
|
GroupSubject: params.msg.groupSubject,
|
|
@@ -363,6 +373,24 @@ export async function processMessage(params) {
|
|
|
363
373
|
? !params.cfg.channels.whatsapp.blockStreaming
|
|
364
374
|
: undefined,
|
|
365
375
|
onModelSelected: prefixContext.onModelSelected,
|
|
376
|
+
// For media messages the inbound archive hook was deferred until after
|
|
377
|
+
// media understanding resolves. Fire it now with the resolved text.
|
|
378
|
+
...(hasMediaAttachment
|
|
379
|
+
? {
|
|
380
|
+
onMediaResolved: () => {
|
|
381
|
+
const raw = params.msg.body ?? "";
|
|
382
|
+
// Prefer the raw transcript for audio. For other media or failed
|
|
383
|
+
// transcription fall back to the resolved Body (which contains the
|
|
384
|
+
// description or a human-readable failure reason). The regex guard
|
|
385
|
+
// prevents using the envelope-formatted Body for plain text messages.
|
|
386
|
+
const resolvedText = ctxPayload.Transcript ??
|
|
387
|
+
(/^<media:[^>]+>/i.test(raw.trim())
|
|
388
|
+
? String(ctxPayload.Body ?? raw)
|
|
389
|
+
: raw);
|
|
390
|
+
fireInboundArchiveHook(String(resolvedText));
|
|
391
|
+
},
|
|
392
|
+
}
|
|
393
|
+
: {}),
|
|
366
394
|
},
|
|
367
395
|
});
|
|
368
396
|
if (!queuedFinal) {
|
|
@@ -230,6 +230,7 @@ export async function monitorWebInbox(options) {
|
|
|
230
230
|
const replyContext = describeReplyContext(msg.message);
|
|
231
231
|
let mediaPath;
|
|
232
232
|
let mediaType;
|
|
233
|
+
let mediaDownloadFailed = false;
|
|
233
234
|
try {
|
|
234
235
|
const inboundMedia = await downloadInboundMedia(msg, sock);
|
|
235
236
|
if (inboundMedia) {
|
|
@@ -241,9 +242,15 @@ export async function monitorWebInbox(options) {
|
|
|
241
242
|
mediaPath = saved.path;
|
|
242
243
|
mediaType = inboundMedia.mimetype;
|
|
243
244
|
}
|
|
245
|
+
else if (body.includes("<media:")) {
|
|
246
|
+
// downloadInboundMedia returned undefined — media was expected but unavailable
|
|
247
|
+
mediaDownloadFailed = true;
|
|
248
|
+
inboundLogger.warn({ id, from, body }, "media download returned empty");
|
|
249
|
+
}
|
|
244
250
|
}
|
|
245
251
|
catch (err) {
|
|
246
|
-
|
|
252
|
+
mediaDownloadFailed = body.includes("<media:");
|
|
253
|
+
inboundLogger.warn({ id, from, error: String(err) }, "media download failed");
|
|
247
254
|
}
|
|
248
255
|
const chatJid = remoteJid;
|
|
249
256
|
const sendComposing = async () => {
|
|
@@ -294,6 +301,7 @@ export async function monitorWebInbox(options) {
|
|
|
294
301
|
sendMedia,
|
|
295
302
|
mediaPath,
|
|
296
303
|
mediaType,
|
|
304
|
+
mediaDownloadFailed,
|
|
297
305
|
};
|
|
298
306
|
try {
|
|
299
307
|
const task = Promise.resolve(debouncer.enqueue(inboundMessage));
|
|
File without changes
|
|
@@ -15,7 +15,7 @@ else
|
|
|
15
15
|
export NODE_PATH="/Users/neo/taskmaster-dev/dist/node_modules:/Users/neo/taskmaster-dev/node_modules:/Users/neo/node_modules:/Users/node_modules:/node_modules:/Users/neo/taskmaster-dev/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir
|
|
18
|
+
exec "$basedir/node" "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
19
19
|
else
|
|
20
|
-
exec node "$basedir
|
|
20
|
+
exec node "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
21
21
|
fi
|
|
@@ -15,7 +15,7 @@ else
|
|
|
15
15
|
export NODE_PATH="/Users/neo/taskmaster-dev/dist/node_modules:/Users/neo/taskmaster-dev/node_modules:/Users/neo/node_modules:/Users/node_modules:/node_modules:/Users/neo/taskmaster-dev/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir
|
|
18
|
+
exec "$basedir/node" "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
19
19
|
else
|
|
20
|
-
exec node "$basedir
|
|
20
|
+
exec node "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
21
21
|
fi
|
|
File without changes
|
|
@@ -15,7 +15,7 @@ else
|
|
|
15
15
|
export NODE_PATH="/Users/neo/taskmaster-dev/dist/node_modules:/Users/neo/taskmaster-dev/node_modules:/Users/neo/node_modules:/Users/node_modules:/node_modules:/Users/neo/taskmaster-dev/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir
|
|
18
|
+
exec "$basedir/node" "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
19
19
|
else
|
|
20
|
-
exec node "$basedir
|
|
20
|
+
exec node "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
21
21
|
fi
|
|
File without changes
|
|
File without changes
|
|
@@ -15,7 +15,7 @@ else
|
|
|
15
15
|
export NODE_PATH="/Users/neo/taskmaster-dev/dist/node_modules:/Users/neo/taskmaster-dev/node_modules:/Users/neo/node_modules:/Users/node_modules:/node_modules:/Users/neo/taskmaster-dev/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir
|
|
18
|
+
exec "$basedir/node" "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
19
19
|
else
|
|
20
|
-
exec node "$basedir
|
|
20
|
+
exec node "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
21
21
|
fi
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@microsoft/agents-hosting": "^1.2.2",
|
|
30
30
|
"@microsoft/agents-hosting-express": "^1.2.2",
|
|
31
31
|
"@microsoft/agents-hosting-extensions-teams": "^1.2.2",
|
|
32
|
-
"taskmaster": "workspace:*",
|
|
32
|
+
"@rubytech/taskmaster": "workspace:*",
|
|
33
33
|
"express": "^5.2.1",
|
|
34
34
|
"proper-lockfile": "^4.1.2"
|
|
35
35
|
}
|
|
@@ -15,7 +15,7 @@ else
|
|
|
15
15
|
export NODE_PATH="/Users/neo/taskmaster-dev/dist/node_modules:/Users/neo/taskmaster-dev/node_modules:/Users/neo/node_modules:/Users/node_modules:/node_modules:/Users/neo/taskmaster-dev/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir
|
|
18
|
+
exec "$basedir/node" "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
19
19
|
else
|
|
20
|
-
exec node "$basedir
|
|
20
|
+
exec node "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
21
21
|
fi
|
|
File without changes
|
|
File without changes
|
|
@@ -15,7 +15,7 @@ else
|
|
|
15
15
|
export NODE_PATH="/Users/neo/taskmaster-dev/dist/node_modules:/Users/neo/taskmaster-dev/node_modules:/Users/neo/node_modules:/Users/node_modules:/node_modules:/Users/neo/taskmaster-dev/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir
|
|
18
|
+
exec "$basedir/node" "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
19
19
|
else
|
|
20
|
-
exec node "$basedir
|
|
20
|
+
exec node "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
21
21
|
fi
|
|
@@ -15,7 +15,7 @@ else
|
|
|
15
15
|
export NODE_PATH="/Users/neo/taskmaster-dev/dist/node_modules:/Users/neo/taskmaster-dev/node_modules:/Users/neo/node_modules:/Users/node_modules:/node_modules:/Users/neo/taskmaster-dev/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
|
-
exec "$basedir/node" "$basedir
|
|
18
|
+
exec "$basedir/node" "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
19
19
|
else
|
|
20
|
-
exec node "$basedir
|
|
20
|
+
exec node "$basedir/../@rubytech/taskmaster/dist/entry.js" "$@"
|
|
21
21
|
fi
|