agent-nuvira 3.3.3 → 3.3.4
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/README.md +13 -5
- package/dist/agent-sdk/src/agent.d.ts +2 -0
- package/dist/agent-sdk/src/agent.d.ts.map +1 -1
- package/dist/agent-sdk/src/define.d.ts +64 -0
- package/dist/agent-sdk/src/define.d.ts.map +1 -0
- package/dist/agent-sdk/src/define.js +76 -0
- package/dist/agent-sdk/src/define.js.map +1 -0
- package/dist/agent-sdk/src/index.d.ts +9 -0
- package/dist/agent-sdk/src/index.d.ts.map +1 -1
- package/dist/agent-sdk/src/index.js +9 -0
- package/dist/agent-sdk/src/index.js.map +1 -1
- package/dist/agent-sdk/src/scaffold.d.ts +11 -0
- package/dist/agent-sdk/src/scaffold.d.ts.map +1 -1
- package/dist/agent-sdk/src/scaffold.js +16 -6
- package/dist/agent-sdk/src/scaffold.js.map +1 -1
- package/dist/agents/long-form-plan.d.ts.map +1 -1
- package/dist/agents/long-form-plan.js +2 -1
- package/dist/agents/long-form-plan.js.map +1 -1
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +5 -4
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/cli/agent.d.ts +2 -2
- package/dist/cli/agent.js +10 -10
- package/dist/cli/chat.d.ts +94 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +354 -20
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/cli-program.d.ts.map +1 -1
- package/dist/cli/cli-program.js +5 -0
- package/dist/cli/cli-program.js.map +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +9 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +3 -2
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/edit.js +2 -2
- package/dist/cli/eval.d.ts +17 -0
- package/dist/cli/eval.d.ts.map +1 -1
- package/dist/cli/eval.js +105 -2
- package/dist/cli/eval.js.map +1 -1
- package/dist/cli/execute.d.ts +16 -1
- package/dist/cli/execute.d.ts.map +1 -1
- package/dist/cli/execute.js +192 -22
- package/dist/cli/execute.js.map +1 -1
- package/dist/cli/loop-executor.d.ts +55 -0
- package/dist/cli/loop-executor.d.ts.map +1 -1
- package/dist/cli/loop-executor.js +212 -13
- package/dist/cli/loop-executor.js.map +1 -1
- package/dist/cli/model.d.ts.map +1 -1
- package/dist/cli/model.js +9 -8
- package/dist/cli/model.js.map +1 -1
- package/dist/cli/models.d.ts +1 -1
- package/dist/cli/models.js +4 -4
- package/dist/cli/parity.d.ts +85 -0
- package/dist/cli/parity.d.ts.map +1 -0
- package/dist/cli/parity.js +506 -0
- package/dist/cli/parity.js.map +1 -0
- package/dist/cli/plan.d.ts.map +1 -1
- package/dist/cli/plan.js +2 -1
- package/dist/cli/plan.js.map +1 -1
- package/dist/cli/retrieval.d.ts.map +1 -1
- package/dist/cli/retrieval.js +5 -4
- package/dist/cli/retrieval.js.map +1 -1
- package/dist/cli/sdk.js +4 -4
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli/trace.d.ts.map +1 -1
- package/dist/cli/trace.js +2 -1
- package/dist/cli/trace.js.map +1 -1
- package/dist/cli/workflow.js +2 -2
- package/dist/cli/workflow.js.map +1 -1
- package/dist/config/types.d.ts +44 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/findings/verdicts.d.ts +241 -0
- package/dist/findings/verdicts.d.ts.map +1 -0
- package/dist/findings/verdicts.js +284 -0
- package/dist/findings/verdicts.js.map +1 -0
- package/dist/gateway/adapters.d.ts +65 -0
- package/dist/gateway/adapters.d.ts.map +1 -1
- package/dist/gateway/adapters.js +216 -10
- package/dist/gateway/adapters.js.map +1 -1
- package/dist/gateway/channel-directory.d.ts +31 -0
- package/dist/gateway/channel-directory.d.ts.map +1 -1
- package/dist/gateway/channel-directory.js +40 -0
- package/dist/gateway/channel-directory.js.map +1 -1
- package/dist/gateway/gateway-log.d.ts +1 -1
- package/dist/gateway/gateway-log.d.ts.map +1 -1
- package/dist/gateway/gateway-log.js.map +1 -1
- package/dist/gateway/hooks.d.ts +87 -19
- package/dist/gateway/hooks.d.ts.map +1 -1
- package/dist/gateway/hooks.js +62 -23
- package/dist/gateway/hooks.js.map +1 -1
- package/dist/gateway/inbound-media.d.ts +147 -0
- package/dist/gateway/inbound-media.d.ts.map +1 -0
- package/dist/gateway/inbound-media.js +317 -0
- package/dist/gateway/inbound-media.js.map +1 -0
- package/dist/gateway/inbox.d.ts +8 -1
- package/dist/gateway/inbox.d.ts.map +1 -1
- package/dist/gateway/inbox.js.map +1 -1
- package/dist/gateway/platform-config.d.ts +14 -0
- package/dist/gateway/platform-config.d.ts.map +1 -1
- package/dist/gateway/platform-config.js +26 -8
- package/dist/gateway/platform-config.js.map +1 -1
- package/dist/gateway/realtime.d.ts +114 -0
- package/dist/gateway/realtime.d.ts.map +1 -0
- package/dist/gateway/realtime.js +402 -0
- package/dist/gateway/realtime.js.map +1 -0
- package/dist/gateway/registry.d.ts +31 -0
- package/dist/gateway/registry.d.ts.map +1 -1
- package/dist/gateway/registry.js +224 -4
- package/dist/gateway/registry.js.map +1 -1
- package/dist/gateway/whatsapp/baileys-bridge.d.ts +11 -1
- package/dist/gateway/whatsapp/baileys-bridge.d.ts.map +1 -1
- package/dist/gateway/whatsapp/baileys-bridge.js +123 -4
- package/dist/gateway/whatsapp/baileys-bridge.js.map +1 -1
- package/dist/gateway/whatsapp/bridge.d.ts +6 -2
- package/dist/gateway/whatsapp/bridge.d.ts.map +1 -1
- package/dist/gateway/whatsapp/bridge.js.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/inference/factory.d.ts +14 -0
- package/dist/inference/factory.d.ts.map +1 -1
- package/dist/inference/factory.js +17 -0
- package/dist/inference/factory.js.map +1 -1
- package/dist/inference/groq-adapter.d.ts +2 -0
- package/dist/inference/groq-adapter.d.ts.map +1 -1
- package/dist/inference/groq-adapter.js +16 -6
- package/dist/inference/groq-adapter.js.map +1 -1
- package/dist/inference/tools.d.ts.map +1 -1
- package/dist/inference/tools.js +29 -0
- package/dist/inference/tools.js.map +1 -1
- package/dist/learning/benchmark.d.ts.map +1 -1
- package/dist/learning/benchmark.js +3 -2
- package/dist/learning/benchmark.js.map +1 -1
- package/dist/learning/continuation.d.ts.map +1 -1
- package/dist/learning/continuation.js +2 -1
- package/dist/learning/continuation.js.map +1 -1
- package/dist/learning/cost-tracker.d.ts.map +1 -1
- package/dist/learning/cost-tracker.js +2 -1
- package/dist/learning/cost-tracker.js.map +1 -1
- package/dist/learning/deferred-task.d.ts.map +1 -1
- package/dist/learning/deferred-task.js +13 -4
- package/dist/learning/deferred-task.js.map +1 -1
- package/dist/learning/eval-framework.d.ts.map +1 -1
- package/dist/learning/eval-framework.js +2 -1
- package/dist/learning/eval-framework.js.map +1 -1
- package/dist/learning/long-form.d.ts.map +1 -1
- package/dist/learning/long-form.js +2 -1
- package/dist/learning/long-form.js.map +1 -1
- package/dist/learning/model-registry.d.ts.map +1 -1
- package/dist/learning/model-registry.js +2 -1
- package/dist/learning/model-registry.js.map +1 -1
- package/dist/learning/reasoning-cache.d.ts.map +1 -1
- package/dist/learning/reasoning-cache.js +2 -1
- package/dist/learning/reasoning-cache.js.map +1 -1
- package/dist/learning/reasoning-trace.d.ts +37 -1
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +66 -0
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/learning/resilient-call.d.ts.map +1 -1
- package/dist/learning/resilient-call.js +2 -1
- package/dist/learning/resilient-call.js.map +1 -1
- package/dist/learning/retrieval.d.ts.map +1 -1
- package/dist/learning/retrieval.js +2 -1
- package/dist/learning/retrieval.js.map +1 -1
- package/dist/learning/seeded-benchmark.d.ts +160 -0
- package/dist/learning/seeded-benchmark.d.ts.map +1 -0
- package/dist/learning/seeded-benchmark.js +321 -0
- package/dist/learning/seeded-benchmark.js.map +1 -0
- package/dist/learning/seeded-bugs.d.ts +142 -0
- package/dist/learning/seeded-bugs.d.ts.map +1 -0
- package/dist/learning/seeded-bugs.js +535 -0
- package/dist/learning/seeded-bugs.js.map +1 -0
- package/dist/learning/step-checkpoint.d.ts +127 -0
- package/dist/learning/step-checkpoint.d.ts.map +1 -0
- package/dist/learning/step-checkpoint.js +244 -0
- package/dist/learning/step-checkpoint.js.map +1 -0
- package/dist/nlu/intent-confirm.d.ts +23 -1
- package/dist/nlu/intent-confirm.d.ts.map +1 -1
- package/dist/nlu/intent-confirm.js +85 -2
- package/dist/nlu/intent-confirm.js.map +1 -1
- package/dist/nlu/learnings.d.ts +7 -0
- package/dist/nlu/learnings.d.ts.map +1 -1
- package/dist/nlu/learnings.js +7 -0
- package/dist/nlu/learnings.js.map +1 -1
- package/dist/observability/debug-log.d.ts +250 -0
- package/dist/observability/debug-log.d.ts.map +1 -0
- package/dist/observability/debug-log.js +500 -0
- package/dist/observability/debug-log.js.map +1 -0
- package/dist/observability/event-bus.d.ts.map +1 -1
- package/dist/observability/event-bus.js +4 -1
- package/dist/observability/event-bus.js.map +1 -1
- package/dist/observability/otel.d.ts +278 -0
- package/dist/observability/otel.d.ts.map +1 -0
- package/dist/observability/otel.js +590 -0
- package/dist/observability/otel.js.map +1 -0
- package/dist/parity/drivers.d.ts +99 -0
- package/dist/parity/drivers.d.ts.map +1 -0
- package/dist/parity/drivers.js +1362 -0
- package/dist/parity/drivers.js.map +1 -0
- package/dist/parity/graph.d.ts +73 -0
- package/dist/parity/graph.d.ts.map +1 -0
- package/dist/parity/graph.js +162 -0
- package/dist/parity/graph.js.map +1 -0
- package/dist/parity/matrix.d.ts +105 -0
- package/dist/parity/matrix.d.ts.map +1 -0
- package/dist/parity/matrix.js +352 -0
- package/dist/parity/matrix.js.map +1 -0
- package/dist/parity/observation.d.ts +444 -0
- package/dist/parity/observation.d.ts.map +1 -0
- package/dist/parity/observation.js +333 -0
- package/dist/parity/observation.js.map +1 -0
- package/dist/parity/scenarios.d.ts +229 -0
- package/dist/parity/scenarios.d.ts.map +1 -0
- package/dist/parity/scenarios.js +175 -0
- package/dist/parity/scenarios.js.map +1 -0
- package/dist/parity/surfaces.d.ts +122 -0
- package/dist/parity/surfaces.d.ts.map +1 -0
- package/dist/parity/surfaces.js +190 -0
- package/dist/parity/surfaces.js.map +1 -0
- package/dist/runtime/fault-injection.d.ts +173 -0
- package/dist/runtime/fault-injection.d.ts.map +1 -0
- package/dist/runtime/fault-injection.js +281 -0
- package/dist/runtime/fault-injection.js.map +1 -0
- package/dist/tools/child-agent-entry.d.ts +23 -0
- package/dist/tools/child-agent-entry.d.ts.map +1 -0
- package/dist/tools/child-agent-entry.js +129 -0
- package/dist/tools/child-agent-entry.js.map +1 -0
- package/dist/tools/child-agent-runtime.d.ts +124 -0
- package/dist/tools/child-agent-runtime.d.ts.map +1 -0
- package/dist/tools/child-agent-runtime.js +704 -0
- package/dist/tools/child-agent-runtime.js.map +1 -0
- package/dist/tools/coding-tools.d.ts.map +1 -1
- package/dist/tools/coding-tools.js +82 -10
- package/dist/tools/coding-tools.js.map +1 -1
- package/dist/tools/delegation-system.d.ts +31 -0
- package/dist/tools/delegation-system.d.ts.map +1 -1
- package/dist/tools/delegation-system.js +70 -9
- package/dist/tools/delegation-system.js.map +1 -1
- package/dist/tools/extract/docx.d.ts +27 -0
- package/dist/tools/extract/docx.d.ts.map +1 -0
- package/dist/tools/extract/docx.js +48 -0
- package/dist/tools/extract/docx.js.map +1 -0
- package/dist/tools/extract/html-text.d.ts +27 -0
- package/dist/tools/extract/html-text.d.ts.map +1 -0
- package/dist/tools/extract/html-text.js +86 -0
- package/dist/tools/extract/html-text.js.map +1 -0
- package/dist/tools/extract/pdf-ocr.d.ts +58 -0
- package/dist/tools/extract/pdf-ocr.d.ts.map +1 -0
- package/dist/tools/extract/pdf-ocr.js +116 -0
- package/dist/tools/extract/pdf-ocr.js.map +1 -0
- package/dist/tools/extract/pdf.d.ts +65 -0
- package/dist/tools/extract/pdf.d.ts.map +1 -0
- package/dist/tools/extract/pdf.js +197 -0
- package/dist/tools/extract/pdf.js.map +1 -0
- package/dist/tools/extract/pptx.d.ts +32 -0
- package/dist/tools/extract/pptx.d.ts.map +1 -0
- package/dist/tools/extract/pptx.js +77 -0
- package/dist/tools/extract/pptx.js.map +1 -0
- package/dist/tools/extract/xlsx.d.ts +47 -0
- package/dist/tools/extract/xlsx.d.ts.map +1 -0
- package/dist/tools/extract/xlsx.js +111 -0
- package/dist/tools/extract/xlsx.js.map +1 -0
- package/dist/tools/finding-tool.d.ts +76 -0
- package/dist/tools/finding-tool.d.ts.map +1 -0
- package/dist/tools/finding-tool.js +125 -0
- package/dist/tools/finding-tool.js.map +1 -0
- package/dist/tools/messaging-tools.d.ts +41 -11
- package/dist/tools/messaging-tools.d.ts.map +1 -1
- package/dist/tools/messaging-tools.js +104 -55
- package/dist/tools/messaging-tools.js.map +1 -1
- package/dist/tools/neutts-synth.d.ts +27 -3
- package/dist/tools/neutts-synth.d.ts.map +1 -1
- package/dist/tools/neutts-synth.js +57 -13
- package/dist/tools/neutts-synth.js.map +1 -1
- package/dist/tools/pipeline-tool.d.ts +43 -1
- package/dist/tools/pipeline-tool.d.ts.map +1 -1
- package/dist/tools/pipeline-tool.js +13 -2
- package/dist/tools/pipeline-tool.js.map +1 -1
- package/dist/tools/read-extract.d.ts +116 -45
- package/dist/tools/read-extract.d.ts.map +1 -1
- package/dist/tools/read-extract.js +494 -158
- package/dist/tools/read-extract.js.map +1 -1
- package/dist/tools/registry.d.ts +2 -2
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +152 -23
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/subagent-refusal.d.ts +15 -0
- package/dist/tools/subagent-refusal.d.ts.map +1 -0
- package/dist/tools/subagent-refusal.js +18 -0
- package/dist/tools/subagent-refusal.js.map +1 -0
- package/dist/tools/subagent-spawner.d.ts +122 -0
- package/dist/tools/subagent-spawner.d.ts.map +1 -1
- package/dist/tools/subagent-spawner.js +249 -28
- package/dist/tools/subagent-spawner.js.map +1 -1
- package/dist/tools/tool-hooks.d.ts +177 -0
- package/dist/tools/tool-hooks.d.ts.map +1 -0
- package/dist/tools/tool-hooks.js +427 -0
- package/dist/tools/tool-hooks.js.map +1 -0
- package/dist/tools/tool-loop.d.ts +82 -0
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +167 -9
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/tools/tool-refusal.d.ts +68 -0
- package/dist/tools/tool-refusal.d.ts.map +1 -0
- package/dist/tools/tool-refusal.js +78 -0
- package/dist/tools/tool-refusal.js.map +1 -0
- package/dist/tools/toolsets.d.ts +8 -0
- package/dist/tools/toolsets.d.ts.map +1 -1
- package/dist/tools/toolsets.js +12 -2
- package/dist/tools/toolsets.js.map +1 -1
- package/dist/tools/vision-tools.d.ts +88 -83
- package/dist/tools/vision-tools.d.ts.map +1 -1
- package/dist/tools/vision-tools.js +134 -103
- package/dist/tools/vision-tools.js.map +1 -1
- package/dist/tools/worktree.d.ts +210 -0
- package/dist/tools/worktree.d.ts.map +1 -0
- package/dist/tools/worktree.js +374 -0
- package/dist/tools/worktree.js.map +1 -0
- package/dist/utils/format.d.ts +3 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +32 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/web-dashboard/attachment-extract.d.ts +64 -0
- package/dist/web-dashboard/attachment-extract.d.ts.map +1 -0
- package/dist/web-dashboard/attachment-extract.js +154 -0
- package/dist/web-dashboard/attachment-extract.js.map +1 -0
- package/dist/web-dashboard/chat-console.d.ts +108 -1
- package/dist/web-dashboard/chat-console.d.ts.map +1 -1
- package/dist/web-dashboard/chat-console.js +36 -0
- package/dist/web-dashboard/chat-console.js.map +1 -1
- package/dist/web-dashboard/hub-data.d.ts +37 -0
- package/dist/web-dashboard/hub-data.d.ts.map +1 -1
- package/dist/web-dashboard/hub-data.js +61 -1
- package/dist/web-dashboard/hub-data.js.map +1 -1
- package/dist/web-dashboard/server.d.ts +14 -0
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +247 -13
- package/dist/web-dashboard/server.js.map +1 -1
- package/dist/web-dashboard/src/types.d.ts +196 -48
- package/dist/web-dashboard/src/types.d.ts.map +1 -1
- package/package.json +18 -3
- package/src/web-dashboard/public/assets/{index-Cyd6tIew.css → index-XJjj2cBX.css} +1 -1
- package/src/web-dashboard/public/assets/index-YWF9FpwQ.js +207 -0
- package/src/web-dashboard/public/assets/index-YWF9FpwQ.js.map +1 -0
- package/src/web-dashboard/public/index.html +2 -2
- package/dist/tools/child-agent-worker.js +0 -212
- package/src/web-dashboard/public/assets/index-CxDj7p6i.js +0 -207
- package/src/web-dashboard/public/assets/index-CxDj7p6i.js.map +0 -1
package/dist/gateway/adapters.js
CHANGED
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { createServer } from 'node:http';
|
|
16
16
|
import { envBuff } from '../config/paths.js';
|
|
17
|
+
import { logger } from '../utils/logger.js';
|
|
17
18
|
import { createHmac, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
18
19
|
import { connect as netConnect } from 'node:net';
|
|
19
20
|
import { connect as tlsConnect } from 'node:tls';
|
|
20
21
|
import { isPlatformConfigured } from './channel-directory.js';
|
|
21
22
|
import { BaileysBridge, isSelfChatEnabled } from './whatsapp/baileys-bridge.js';
|
|
22
23
|
import { hasWhatsAppSession } from './whatsapp/session.js';
|
|
24
|
+
import { downloadInboundAttachment, MAX_INBOUND_MEDIA_BYTES, } from './inbound-media.js';
|
|
23
25
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
24
26
|
/** POST JSON to an endpoint and treat 2xx as success. */
|
|
25
27
|
async function postJson(url, body, token, extraHeaders) {
|
|
@@ -109,18 +111,27 @@ export class TelegramAdapter {
|
|
|
109
111
|
const data = (await res.json());
|
|
110
112
|
for (const u of data.result ?? []) {
|
|
111
113
|
this.offset = u.update_id + 1;
|
|
112
|
-
const
|
|
113
|
-
if (!
|
|
114
|
+
const m = u.message;
|
|
115
|
+
if (!m?.chat)
|
|
114
116
|
continue;
|
|
115
|
-
|
|
117
|
+
// A document/photo carries its instruction in the CAPTION; reading
|
|
118
|
+
// only `text` made those messages look empty and they were dropped.
|
|
119
|
+
const text = m.text ?? m.caption ?? '';
|
|
120
|
+
const media = await this.inboundMedia(m);
|
|
121
|
+
if (!text && !media)
|
|
122
|
+
continue;
|
|
123
|
+
const chat = m.chat;
|
|
116
124
|
await this.handler?.({
|
|
117
125
|
platform: 'telegram',
|
|
118
126
|
channelId: String(chat.id),
|
|
119
127
|
text,
|
|
120
|
-
from:
|
|
128
|
+
from: m.from?.first_name ?? 'telegram-user',
|
|
121
129
|
// P1: real sender id + group detection (chat.type).
|
|
122
|
-
senderId:
|
|
130
|
+
senderId: m.from?.id !== undefined ? String(m.from.id) : undefined,
|
|
123
131
|
isGroup: chat.type !== undefined && chat.type !== 'private',
|
|
132
|
+
// The downloaded attachment rides through the SAME hydration path
|
|
133
|
+
// the WhatsApp bridge uses (registry → read_extract).
|
|
134
|
+
...(media ? { media } : {}),
|
|
124
135
|
});
|
|
125
136
|
}
|
|
126
137
|
}
|
|
@@ -130,6 +141,72 @@ export class TelegramAdapter {
|
|
|
130
141
|
};
|
|
131
142
|
void loop();
|
|
132
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Download an inbound attachment's bytes via `getFile` + the file endpoint.
|
|
146
|
+
* Never throws — returns null on any failure (or when the file is larger than
|
|
147
|
+
* the extraction cap), so the turn still runs with its caption text.
|
|
148
|
+
*/
|
|
149
|
+
async downloadFile(fileId, declaredSize) {
|
|
150
|
+
if (declaredSize !== undefined && declaredSize > MAX_INBOUND_MEDIA_BYTES)
|
|
151
|
+
return null;
|
|
152
|
+
try {
|
|
153
|
+
const res = await this.api('getFile', { file_id: fileId });
|
|
154
|
+
if (!res.ok)
|
|
155
|
+
return null;
|
|
156
|
+
const data = (await res.json());
|
|
157
|
+
const filePath = data.result?.file_path;
|
|
158
|
+
if (!filePath)
|
|
159
|
+
return null;
|
|
160
|
+
// Telegram caps bot downloads at 20 MB; the declared size is checked
|
|
161
|
+
// BEFORE the body is fetched where it is known.
|
|
162
|
+
const size = data.result?.file_size;
|
|
163
|
+
if (size !== undefined && size > MAX_INBOUND_MEDIA_BYTES)
|
|
164
|
+
return null;
|
|
165
|
+
const fileRes = await fetch(`https://api.telegram.org/file/bot${this.token}/${filePath}`);
|
|
166
|
+
if (!fileRes.ok)
|
|
167
|
+
return null;
|
|
168
|
+
const buf = new Uint8Array(await fileRes.arrayBuffer());
|
|
169
|
+
if (buf.byteLength === 0 || buf.byteLength > MAX_INBOUND_MEDIA_BYTES)
|
|
170
|
+
return null;
|
|
171
|
+
return buf;
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Build the inbound media payload from a Telegram message, or null when it
|
|
179
|
+
* carries none. Photos arrive as several sizes — the largest is used.
|
|
180
|
+
*/
|
|
181
|
+
async inboundMedia(m) {
|
|
182
|
+
if (m.document) {
|
|
183
|
+
const data = await this.downloadFile(m.document.file_id, m.document.file_size);
|
|
184
|
+
return data
|
|
185
|
+
? { type: 'document', data, filename: m.document.file_name, mimetype: m.document.mime_type, caption: m.caption }
|
|
186
|
+
: null;
|
|
187
|
+
}
|
|
188
|
+
if (m.video) {
|
|
189
|
+
const data = await this.downloadFile(m.video.file_id, m.video.file_size);
|
|
190
|
+
return data
|
|
191
|
+
? { type: 'video', data, filename: m.video.file_name, mimetype: m.video.mime_type, caption: m.caption }
|
|
192
|
+
: null;
|
|
193
|
+
}
|
|
194
|
+
if (m.audio) {
|
|
195
|
+
const data = await this.downloadFile(m.audio.file_id, m.audio.file_size);
|
|
196
|
+
return data
|
|
197
|
+
? { type: 'audio', data, filename: m.audio.file_name, mimetype: m.audio.mime_type, caption: m.caption }
|
|
198
|
+
: null;
|
|
199
|
+
}
|
|
200
|
+
const photo = Array.isArray(m.photo) && m.photo.length > 0 ? m.photo[m.photo.length - 1] : undefined;
|
|
201
|
+
if (photo) {
|
|
202
|
+
const data = await this.downloadFile(photo.file_id, photo.file_size);
|
|
203
|
+
// Telegram photos carry no filename; supply the MIME type so the sandbox
|
|
204
|
+
// name keeps an image extension (which routes to the describe_image hint
|
|
205
|
+
// rather than an extractor refusal).
|
|
206
|
+
return data ? { type: 'image', data, filename: 'photo.jpg', mimetype: 'image/jpeg', caption: m.caption } : null;
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
133
210
|
async stop() {
|
|
134
211
|
this.running = false;
|
|
135
212
|
if (this.timer)
|
|
@@ -213,9 +290,36 @@ export class DiscordAdapter extends WebhookChannelAdapter {
|
|
|
213
290
|
this.token = envBuff('DISCORD_BOT_TOKEN') ?? '';
|
|
214
291
|
this.configured = Boolean(this.webhookUrl || this.token);
|
|
215
292
|
}
|
|
293
|
+
/** The real-time Gateway connection, when a bot token is configured. */
|
|
294
|
+
gateway = null;
|
|
216
295
|
describe() {
|
|
217
296
|
return this.configured ? 'Discord (webhook)' : 'Discord (not configured)';
|
|
218
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Inbound Discord messages arrive over the Gateway WebSocket (a bot cannot
|
|
300
|
+
* own the Discord webhook), so a bot token starts the real-time source. The
|
|
301
|
+
* shared webhook receiver still serves outbound-only webhook setups.
|
|
302
|
+
*/
|
|
303
|
+
async start(onMessage) {
|
|
304
|
+
if (!this.token) {
|
|
305
|
+
// Said out loud: without a bot token there is no socket, and a gateway that
|
|
306
|
+
// answers outbound but never receives is otherwise indistinguishable from
|
|
307
|
+
// one whose inbound is broken. Names the key and what still works.
|
|
308
|
+
logger.info('Discord: real-time Gateway inbound skipped — no bot token (BUFF_DISCORD_BOT_TOKEN). ' +
|
|
309
|
+
'Webhook inbound (public URL required) and outbound both still work.');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const { DiscordGatewaySource } = await import('./realtime.js');
|
|
313
|
+
const source = new DiscordGatewaySource({ token: this.token });
|
|
314
|
+
this.gateway = source;
|
|
315
|
+
void source.start(onMessage).catch(() => {
|
|
316
|
+
// start() only rejects on a programming error; handshakes retry internally.
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
async stop() {
|
|
320
|
+
await this.gateway?.stop();
|
|
321
|
+
this.gateway = null;
|
|
322
|
+
}
|
|
219
323
|
sendUrl(channelId) {
|
|
220
324
|
// A webhook URL may embed its own channel; an explicit channelId with a
|
|
221
325
|
// bot token uses the REST API. When only the webhook URL exists, send there.
|
|
@@ -266,9 +370,37 @@ export class SlackAdapter extends WebhookChannelAdapter {
|
|
|
266
370
|
this.token = envBuff('SLACK_BOT_TOKEN') ?? '';
|
|
267
371
|
this.configured = Boolean(this.webhookUrl || this.token);
|
|
268
372
|
}
|
|
373
|
+
/** The Socket Mode connection, when an app-level token is configured. */
|
|
374
|
+
socketMode = null;
|
|
269
375
|
describe() {
|
|
270
376
|
return this.configured ? 'Slack (webhook)' : 'Slack (not configured)';
|
|
271
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* Slack Events API inbound needs a public HTTPS endpoint; apps that cannot
|
|
380
|
+
* expose one use Socket Mode, which carries the same `event_callback`
|
|
381
|
+
* envelopes. Enabled by an app-level token (`SLACK_APP_TOKEN`, `xapp-…`).
|
|
382
|
+
*/
|
|
383
|
+
async start(onMessage) {
|
|
384
|
+
const appToken = envBuff('SLACK_APP_TOKEN') ?? '';
|
|
385
|
+
if (!appToken) {
|
|
386
|
+
logger.info('Slack: real-time Socket Mode inbound skipped — no app-level token (BUFF_SLACK_APP_TOKEN). ' +
|
|
387
|
+
'The Events API path (public Request URL) and outbound both still work.');
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const { SlackSocketModeSource } = await import('./realtime.js');
|
|
391
|
+
const source = new SlackSocketModeSource({
|
|
392
|
+
appToken,
|
|
393
|
+
botToken: this.token || undefined,
|
|
394
|
+
});
|
|
395
|
+
this.socketMode = source;
|
|
396
|
+
void source.start(onMessage).catch(() => {
|
|
397
|
+
// Handshake failures retry with backoff inside the source.
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
async stop() {
|
|
401
|
+
await this.socketMode?.stop();
|
|
402
|
+
this.socketMode = null;
|
|
403
|
+
}
|
|
272
404
|
sendUrl(channelId) {
|
|
273
405
|
if (this.webhookUrl)
|
|
274
406
|
return this.webhookUrl;
|
|
@@ -334,7 +466,7 @@ export class WhatsAppBridgeAdapter {
|
|
|
334
466
|
}
|
|
335
467
|
async start(onMessage) {
|
|
336
468
|
this.handler = onMessage;
|
|
337
|
-
await this.bridge.connect((fromJid, text, participant, messageId) => {
|
|
469
|
+
await this.bridge.connect((fromJid, text, participant, messageId, media) => {
|
|
338
470
|
// `||` (not `??`): Baileys 7 can deliver participant as an EMPTY string
|
|
339
471
|
// for DMs — an empty string is not nullish, so `??` would blank the
|
|
340
472
|
// sender id and the policy gate would refuse every sender.
|
|
@@ -350,6 +482,8 @@ export class WhatsAppBridgeAdapter {
|
|
|
350
482
|
// Idempotency: WhatsApp's `key.id` is stable across re-deliveries, so
|
|
351
483
|
// the gateway collapses the bridge's offline backfill into one turn.
|
|
352
484
|
messageId,
|
|
485
|
+
// A downloaded document/image (the caption already folded into `text`).
|
|
486
|
+
...(media ? { media } : {}),
|
|
353
487
|
});
|
|
354
488
|
});
|
|
355
489
|
}
|
|
@@ -1954,6 +2088,29 @@ export class HomeAssistantAdapter {
|
|
|
1954
2088
|
return postJson(`${this.opts.url}/api/services/persistent_notification/create`, { title: 'Agent-Nuvira', message }, this.opts.token);
|
|
1955
2089
|
}
|
|
1956
2090
|
}
|
|
2091
|
+
/**
|
|
2092
|
+
* Classify an attachment by MIME type, falling back to its filename extension.
|
|
2093
|
+
* Exported for the real-time transports (`realtime.ts`), which receive Discord
|
|
2094
|
+
* `content_type` / Slack `mimetype` on a live gateway event rather than a
|
|
2095
|
+
* webhook body.
|
|
2096
|
+
*/
|
|
2097
|
+
export function mediaKindFromMime(mimetype, filename) {
|
|
2098
|
+
const mt = (mimetype ?? '').toLowerCase();
|
|
2099
|
+
if (mt.startsWith('image/'))
|
|
2100
|
+
return 'image';
|
|
2101
|
+
if (mt.startsWith('audio/'))
|
|
2102
|
+
return 'audio';
|
|
2103
|
+
if (mt.startsWith('video/'))
|
|
2104
|
+
return 'video';
|
|
2105
|
+
const ext = (filename ?? '').toLowerCase().split('.').pop() ?? '';
|
|
2106
|
+
if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'tiff'].includes(ext))
|
|
2107
|
+
return 'image';
|
|
2108
|
+
if (['mp3', 'm4a', 'ogg', 'oga', 'opus', 'wav', 'flac'].includes(ext))
|
|
2109
|
+
return 'audio';
|
|
2110
|
+
if (['mp4', 'mov', 'mkv', 'webm'].includes(ext))
|
|
2111
|
+
return 'video';
|
|
2112
|
+
return 'document';
|
|
2113
|
+
}
|
|
1957
2114
|
/**
|
|
1958
2115
|
* Parse platform-specific webhook bodies into a normal InboundMessage.
|
|
1959
2116
|
* Returns null when the payload isn't a user message (e.g. Slack challenge).
|
|
@@ -1963,13 +2120,26 @@ export function parseWebhookPayload(platform, body) {
|
|
|
1963
2120
|
if (platform === 'discord') {
|
|
1964
2121
|
const content = body?.content;
|
|
1965
2122
|
const channelId = body?.channel_id;
|
|
1966
|
-
|
|
2123
|
+
const first = (Array.isArray(body?.attachments) ? body.attachments[0] : undefined);
|
|
2124
|
+
const hasMedia = Boolean(first?.url);
|
|
2125
|
+
// A media-only message has an EMPTY `content` — it must still dispatch so
|
|
2126
|
+
// the attachment is downloaded and extracted, not dropped for lacking text.
|
|
2127
|
+
if ((typeof content === 'string' || hasMedia) && channelId) {
|
|
1967
2128
|
return {
|
|
1968
2129
|
channelId,
|
|
1969
|
-
text: content,
|
|
2130
|
+
text: typeof content === 'string' ? content : '',
|
|
1970
2131
|
from: body?.author?.username ?? 'discord-user',
|
|
1971
2132
|
// P1: author id (webhooks can't tell DM vs guild channel).
|
|
1972
2133
|
senderId: body?.author?.id !== undefined ? String(body.author.id) : undefined,
|
|
2134
|
+
...(hasMedia && first?.url
|
|
2135
|
+
? {
|
|
2136
|
+
media: {
|
|
2137
|
+
type: mediaKindFromMime(first.content_type, first.filename),
|
|
2138
|
+
attachment: { url: first.url, filename: first.filename, mimetype: first.content_type, size: first.size },
|
|
2139
|
+
caption: typeof content === 'string' ? content : undefined,
|
|
2140
|
+
},
|
|
2141
|
+
}
|
|
2142
|
+
: {}),
|
|
1973
2143
|
};
|
|
1974
2144
|
}
|
|
1975
2145
|
return null;
|
|
@@ -1980,14 +2150,27 @@ export function parseWebhookPayload(platform, body) {
|
|
|
1980
2150
|
if (body?.challenge)
|
|
1981
2151
|
return null;
|
|
1982
2152
|
const event = body?.event;
|
|
1983
|
-
|
|
2153
|
+
const file = (Array.isArray(event?.files) ? event.files[0] : undefined);
|
|
2154
|
+
const fileUrl = file?.url_private_download ?? file?.url_private;
|
|
2155
|
+
const hasMedia = Boolean(fileUrl);
|
|
2156
|
+
if (event?.type === 'message' && event.channel && (typeof event.text === 'string' || hasMedia)) {
|
|
1984
2157
|
return {
|
|
1985
2158
|
channelId: event.channel,
|
|
1986
|
-
text: event.text,
|
|
2159
|
+
text: typeof event.text === 'string' ? event.text : '',
|
|
1987
2160
|
from: event.user ?? 'slack-user',
|
|
1988
2161
|
// P1: event.user IS the Slack user id; D-channels are DMs.
|
|
1989
2162
|
senderId: event.user ?? undefined,
|
|
1990
2163
|
isGroup: typeof event.channel === 'string' && !event.channel.startsWith('D'),
|
|
2164
|
+
...(hasMedia && fileUrl
|
|
2165
|
+
? {
|
|
2166
|
+
media: {
|
|
2167
|
+
type: mediaKindFromMime(file.mimetype, file.name),
|
|
2168
|
+
// Slack files require the bot token on the download URL.
|
|
2169
|
+
attachment: { url: fileUrl, filename: file.name, mimetype: file.mimetype, size: file.size, authenticated: true },
|
|
2170
|
+
caption: typeof event.text === 'string' ? event.text : undefined,
|
|
2171
|
+
},
|
|
2172
|
+
}
|
|
2173
|
+
: {}),
|
|
1991
2174
|
};
|
|
1992
2175
|
}
|
|
1993
2176
|
return null;
|
|
@@ -2075,6 +2258,9 @@ export class WebhookReceiver {
|
|
|
2075
2258
|
res.writeHead(200);
|
|
2076
2259
|
res.end('ok');
|
|
2077
2260
|
if (parsed) {
|
|
2261
|
+
// Download the attachment BEFORE dispatch, so the turn always sees a
|
|
2262
|
+
// fully-hydrated document (the registry extracts it like any other).
|
|
2263
|
+
const media = parsed.media ? await this.downloadWebhookMedia(parsed.media) : null;
|
|
2078
2264
|
await this.handler?.({
|
|
2079
2265
|
platform,
|
|
2080
2266
|
channelId: parsed.channelId,
|
|
@@ -2083,11 +2269,31 @@ export class WebhookReceiver {
|
|
|
2083
2269
|
// P1: real sender id + DM/group detection ride through the payload.
|
|
2084
2270
|
senderId: parsed.senderId,
|
|
2085
2271
|
isGroup: parsed.isGroup,
|
|
2272
|
+
...(media ? { media } : {}),
|
|
2086
2273
|
});
|
|
2087
2274
|
}
|
|
2088
2275
|
});
|
|
2089
2276
|
await new Promise((resolve) => this.server.listen(port, host, resolve));
|
|
2090
2277
|
}
|
|
2278
|
+
/**
|
|
2279
|
+
* Download a webhook attachment's bytes. Never throws — returns null on any
|
|
2280
|
+
* failure, so the text turn still runs. Slack's `url_private*` endpoints need
|
|
2281
|
+
* the bot token; Discord's CDN URL is public.
|
|
2282
|
+
*/
|
|
2283
|
+
async downloadWebhookMedia(media) {
|
|
2284
|
+
const att = media.attachment;
|
|
2285
|
+
const downloaded = await downloadInboundAttachment({
|
|
2286
|
+
type: media.type,
|
|
2287
|
+
url: att.url,
|
|
2288
|
+
filename: att.filename,
|
|
2289
|
+
mimetype: att.mimetype,
|
|
2290
|
+
size: att.size,
|
|
2291
|
+
authenticated: att.authenticated,
|
|
2292
|
+
// Slack's url_private* endpoints need the bot token.
|
|
2293
|
+
token: att.authenticated ? (envBuff('SLACK_BOT_TOKEN') ?? '') : undefined,
|
|
2294
|
+
});
|
|
2295
|
+
return downloaded ? { ...downloaded, caption: media.caption } : null;
|
|
2296
|
+
}
|
|
2091
2297
|
/**
|
|
2092
2298
|
* Verify a webhook POST's platform signature. Returns true when the
|
|
2093
2299
|
* platform has NO secret configured (local/trusted setup) or the signature
|