@automagik/omni 2.260704.1 → 2.260704.2
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.
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-- Per-instance first-party cross-instance opt-in.
|
|
2
|
+
--
|
|
3
|
+
-- Adds `instances.allow_first_party` (default false). When true, the agent
|
|
4
|
+
-- dispatcher will NOT drop inbound messages whose sender phone matches another
|
|
5
|
+
-- active instance's owner. This lets a user's "assistant" instance reply to
|
|
6
|
+
-- messages sent from their own personal number (which is another instance's
|
|
7
|
+
-- owner) instead of silently dropping them via loop protection.
|
|
8
|
+
--
|
|
9
|
+
-- Default false preserves current loop-protection behavior for every existing
|
|
10
|
+
-- instance. Opt in per-instance via:
|
|
11
|
+
--
|
|
12
|
+
-- omni instances update <id> --allow-first-party
|
|
13
|
+
--
|
|
14
|
+
-- Does NOT affect the separate "message from self" self-skip — an instance
|
|
15
|
+
-- still never replies to its own outbound. See `isFirstPartyInstanceSender`
|
|
16
|
+
-- and the gate in packages/api/src/plugins/agent-dispatcher.ts.
|
|
17
|
+
--
|
|
18
|
+
-- Hand-written (not `drizzle-kit generate`) to avoid the gupshup column
|
|
19
|
+
-- rename prompts that block non-interactive runs in this repo.
|
|
20
|
+
|
|
21
|
+
ALTER TABLE "instances"
|
|
22
|
+
ADD COLUMN IF NOT EXISTS "allow_first_party" boolean DEFAULT false NOT NULL;
|
|
@@ -267,6 +267,13 @@
|
|
|
267
267
|
"when": 1777950000000,
|
|
268
268
|
"tag": "0037_message_debounce_max_wait",
|
|
269
269
|
"breakpoints": true
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"idx": 38,
|
|
273
|
+
"version": "7",
|
|
274
|
+
"when": 1778040000000,
|
|
275
|
+
"tag": "0038_instances_allow_first_party",
|
|
276
|
+
"breakpoints": true
|
|
270
277
|
}
|
|
271
278
|
]
|
|
272
279
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../src/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../src/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqMpC,wBAAgB,sBAAsB,IAAI,OAAO,CA0vChD"}
|
package/dist/index.js
CHANGED
|
@@ -71672,6 +71672,7 @@ var init_schema2 = __esm(() => {
|
|
|
71672
71672
|
followUpConfig: jsonb("follow_up_config").$type(),
|
|
71673
71673
|
bridgeTmuxSession: text("bridge_tmux_session"),
|
|
71674
71674
|
requireGenieSignature: boolean("require_genie_signature").notNull().default(false),
|
|
71675
|
+
allowFirstParty: boolean("allow_first_party").notNull().default(false),
|
|
71675
71676
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
71676
71677
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
71677
71678
|
}, (table3) => ({
|
|
@@ -125009,7 +125010,7 @@ import { fileURLToPath } from "url";
|
|
|
125009
125010
|
// package.json
|
|
125010
125011
|
var package_default = {
|
|
125011
125012
|
name: "@automagik/omni",
|
|
125012
|
-
version: "2.260704.
|
|
125013
|
+
version: "2.260704.2",
|
|
125013
125014
|
description: "LLM-optimized CLI for Omni",
|
|
125014
125015
|
type: "module",
|
|
125015
125016
|
bin: {
|
|
@@ -132572,6 +132573,7 @@ function applyMiscFields(body, opts) {
|
|
|
132572
132573
|
setBool(body, "twilioValidateSignature", opts.twilioValidateSignature);
|
|
132573
132574
|
setVal(body, "bridgeTmuxSession", opts.bridgeTmuxSession);
|
|
132574
132575
|
setBool(body, "requireGenieSignature", opts.requireGenieSignature);
|
|
132576
|
+
setBool(body, "allowFirstParty", opts.allowFirstParty);
|
|
132575
132577
|
if (opts.triggerEvents !== undefined) {
|
|
132576
132578
|
const raw2 = opts.triggerEvents;
|
|
132577
132579
|
body.triggerEvents = raw2 === "null" ? null : raw2.split(",").map((s) => s.trim());
|
|
@@ -132682,7 +132684,7 @@ function createInstancesCommand() {
|
|
|
132682
132684
|
error(`Failed to get instance: ${message}`, undefined, 3);
|
|
132683
132685
|
}
|
|
132684
132686
|
});
|
|
132685
|
-
instances.command("create").description("Create a new instance (supports all API fields)").requiredOption("--name <name>", "Instance name").requiredOption("--channel <type>", `Channel type (${VALID_CHANNELS2.join(", ")})`).option("--agent-fk-id <uuid>", 'Agent FK UUID (references agents table, use "null" to clear). When set without --reply-filter-mode, reply filter defaults to {mode:"all", onDm:true} so messages are dispatched instead of silently dropped (omni#443).').option("--agent-provider <id>", "Agent provider ID").option("--agent <id>", "Agent ID").option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v) => Number.parseInt(v, 10)).option("--agent-stream-mode", "Enable streaming responses").option("--agent-session-strategy <strategy>", "Session strategy: per_user, per_chat, per_user_per_chat").option("--agent-prefix-sender-name", "Prefix messages with sender name").option("--no-agent-prefix-sender-name", "Disable sender name prefix").option("--agent-wait-for-media", "Wait for media processing before dispatch").option("--no-agent-wait-for-media", "Dispatch immediately without waiting for media").option("--agent-send-media-path", "Include file path in formatted media text").option("--no-agent-send-media-path", "Exclude file path from formatted media text").option("--agent-send-media-path-types <types>", "Content types that receive file path (comma-separated: image,video,document)").option("--reply-filter-mode <mode>", "Reply filter: all or filtered").option("--reply-on-dm", "Reply to DMs").option("--no-reply-on-dm", "Ignore DMs").option("--reply-on-mention", "Reply when @mentioned").option("--no-reply-on-mention", "Ignore @mentions").option("--reply-on-reply", "Reply when message is reply to bot").option("--no-reply-on-reply", "Ignore replies").option("--reply-on-name", "Reply when bot name appears in text").option("--no-reply-on-name", "Ignore name matches").option("--reply-name-patterns <patterns>", "Custom name patterns (comma-separated)").option("--enable-auto-split", "Split responses on double newlines").option("--no-enable-auto-split", "Disable auto-split").option("--message-format-mode <mode>", "Format mode: convert or passthrough").option("--debounce-mode <mode>", "Debounce mode: disabled, fixed, randomized, or presence").option("--debounce-min <ms>", "Minimum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--debounce-group <ms>", "Group chat debounce in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max-wait <ms>", "Presence-mode hard cap in ms (flush even under continuous typing)", (v) => Number.parseInt(v, 10)).option("--split-delay-mode <mode>", "Split delay mode: disabled, fixed, or randomized").option("--split-delay-fixed <ms>", "Fixed delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--agent-gate", "Enable LLM response gate").option("--agent-gate-model <model>", "Model for response gate").option("--agent-gate-prompt <prompt>", "Custom gate prompt").option("--tts-voice <id>", "ElevenLabs voice ID").option("--tts-model <id>", "ElevenLabs model ID").option("--read-receipts <mode>", "Read receipts mode: on, off, or exclude-self").option("--access-mode <mode>", "Access mode: disabled, blocklist, or allowlist").option("--reaction-ack <mode>", "Reaction ack mode (on|off)").option("--reaction-ack-emoji <json>", "Per-channel emoji map as JSON").option("--ack-timeout <ms>", "Ack timeout in milliseconds", (v) => Number.parseInt(v, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v) => Number.parseInt(v, 10)).option("--token <token>", "Generic bot token (auto-resolves to channel-specific field)").option("--telegram-token <token>", "Telegram bot token").option("--discord-token <token>", "Discord bot token").option("--slack-bot-token <token>", "Slack bot token").option("--slack-app-token <token>", "Slack app token").option("--gupshup-callback-url <url>", "Gupshup Custom Integration callback URL").option("--gupshup-auth-token <token>", "Gupshup Custom Integration auth token").option("--gupshup-event-id <id>", "Gupshup event ID (default: nx_omni_agent_reply)").option("--gupshup-webhook-verify-token <token>", "Gupshup webhook verify token").option("--twilio-account-sid <sid>", "Twilio Account SID").option("--twilio-auth-token <token>", "Twilio Auth Token").option("--twilio-from <address>", "Twilio WhatsApp sender address (whatsapp:+E164)").option("--twilio-messaging-service-sid <sid>", "Twilio Messaging Service SID").option("--twilio-status-callback-url <url>", "Twilio outbound status callback URL").option("--twilio-webhook-url <url>", "Public Twilio webhook URL for signature validation").option("--twilio-validate-signature", "Validate X-Twilio-Signature on webhooks").option("--no-twilio-validate-signature", "Disable X-Twilio-Signature validation").option("--bridge-tmux-session <name>", 'Tmux session name the genie bridge spawns into for this instance (propagated as GENIE_TMUX_SESSION via NATS). Use "null" to clear.').option("--require-genie-signature", "Require a verified X-Genie-Signature on requests targeting this instance. Bearer-only requests will be rejected with 401.").option("--no-require-genie-signature", "Allow bearer-only requests targeting this instance (default).").option("--is-default", "Set as default instance for channel").action(async (options) => {
|
|
132687
|
+
instances.command("create").description("Create a new instance (supports all API fields)").requiredOption("--name <name>", "Instance name").requiredOption("--channel <type>", `Channel type (${VALID_CHANNELS2.join(", ")})`).option("--agent-fk-id <uuid>", 'Agent FK UUID (references agents table, use "null" to clear). When set without --reply-filter-mode, reply filter defaults to {mode:"all", onDm:true} so messages are dispatched instead of silently dropped (omni#443).').option("--agent-provider <id>", "Agent provider ID").option("--agent <id>", "Agent ID").option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v) => Number.parseInt(v, 10)).option("--agent-stream-mode", "Enable streaming responses").option("--agent-session-strategy <strategy>", "Session strategy: per_user, per_chat, per_user_per_chat").option("--agent-prefix-sender-name", "Prefix messages with sender name").option("--no-agent-prefix-sender-name", "Disable sender name prefix").option("--agent-wait-for-media", "Wait for media processing before dispatch").option("--no-agent-wait-for-media", "Dispatch immediately without waiting for media").option("--agent-send-media-path", "Include file path in formatted media text").option("--no-agent-send-media-path", "Exclude file path from formatted media text").option("--agent-send-media-path-types <types>", "Content types that receive file path (comma-separated: image,video,document)").option("--reply-filter-mode <mode>", "Reply filter: all or filtered").option("--reply-on-dm", "Reply to DMs").option("--no-reply-on-dm", "Ignore DMs").option("--reply-on-mention", "Reply when @mentioned").option("--no-reply-on-mention", "Ignore @mentions").option("--reply-on-reply", "Reply when message is reply to bot").option("--no-reply-on-reply", "Ignore replies").option("--reply-on-name", "Reply when bot name appears in text").option("--no-reply-on-name", "Ignore name matches").option("--reply-name-patterns <patterns>", "Custom name patterns (comma-separated)").option("--enable-auto-split", "Split responses on double newlines").option("--no-enable-auto-split", "Disable auto-split").option("--message-format-mode <mode>", "Format mode: convert or passthrough").option("--debounce-mode <mode>", "Debounce mode: disabled, fixed, randomized, or presence").option("--debounce-min <ms>", "Minimum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--debounce-group <ms>", "Group chat debounce in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max-wait <ms>", "Presence-mode hard cap in ms (flush even under continuous typing)", (v) => Number.parseInt(v, 10)).option("--split-delay-mode <mode>", "Split delay mode: disabled, fixed, or randomized").option("--split-delay-fixed <ms>", "Fixed delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--agent-gate", "Enable LLM response gate").option("--agent-gate-model <model>", "Model for response gate").option("--agent-gate-prompt <prompt>", "Custom gate prompt").option("--tts-voice <id>", "ElevenLabs voice ID").option("--tts-model <id>", "ElevenLabs model ID").option("--read-receipts <mode>", "Read receipts mode: on, off, or exclude-self").option("--access-mode <mode>", "Access mode: disabled, blocklist, or allowlist").option("--reaction-ack <mode>", "Reaction ack mode (on|off)").option("--reaction-ack-emoji <json>", "Per-channel emoji map as JSON").option("--ack-timeout <ms>", "Ack timeout in milliseconds", (v) => Number.parseInt(v, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v) => Number.parseInt(v, 10)).option("--token <token>", "Generic bot token (auto-resolves to channel-specific field)").option("--telegram-token <token>", "Telegram bot token").option("--discord-token <token>", "Discord bot token").option("--slack-bot-token <token>", "Slack bot token").option("--slack-app-token <token>", "Slack app token").option("--gupshup-callback-url <url>", "Gupshup Custom Integration callback URL").option("--gupshup-auth-token <token>", "Gupshup Custom Integration auth token").option("--gupshup-event-id <id>", "Gupshup event ID (default: nx_omni_agent_reply)").option("--gupshup-webhook-verify-token <token>", "Gupshup webhook verify token").option("--twilio-account-sid <sid>", "Twilio Account SID").option("--twilio-auth-token <token>", "Twilio Auth Token").option("--twilio-from <address>", "Twilio WhatsApp sender address (whatsapp:+E164)").option("--twilio-messaging-service-sid <sid>", "Twilio Messaging Service SID").option("--twilio-status-callback-url <url>", "Twilio outbound status callback URL").option("--twilio-webhook-url <url>", "Public Twilio webhook URL for signature validation").option("--twilio-validate-signature", "Validate X-Twilio-Signature on webhooks").option("--no-twilio-validate-signature", "Disable X-Twilio-Signature validation").option("--bridge-tmux-session <name>", 'Tmux session name the genie bridge spawns into for this instance (propagated as GENIE_TMUX_SESSION via NATS). Use "null" to clear.').option("--require-genie-signature", "Require a verified X-Genie-Signature on requests targeting this instance. Bearer-only requests will be rejected with 401.").option("--no-require-genie-signature", "Allow bearer-only requests targeting this instance (default).").option("--allow-first-party", "Process (do not drop) inbound messages whose sender matches another active instance owner. Lets this instance reply to messages from your own personal number.").option("--no-allow-first-party", "Drop inbound messages whose sender matches another active instance owner (default, loop-protection).").option("--is-default", "Set as default instance for channel").action(async (options) => {
|
|
132686
132688
|
const channel = options.channel;
|
|
132687
132689
|
if (!VALID_CHANNELS2.includes(channel)) {
|
|
132688
132690
|
error(`Invalid channel: ${channel}`, { validChannels: VALID_CHANNELS2 });
|
|
@@ -132950,7 +132952,7 @@ function createInstancesCommand() {
|
|
|
132950
132952
|
throw new Error(err?.error?.message ?? `HTTP ${response.status}`);
|
|
132951
132953
|
}
|
|
132952
132954
|
}
|
|
132953
|
-
instances.command("update <id>").description("Update an instance (supports all API fields)").option("--name <name>", "Instance name").option("--is-default", "Set as default instance for channel").option("--no-is-default", "Unset as default instance for channel").option("--agent-fk-id <uuid>", 'Agent FK UUID (references agents table, use "null" to clear). When assigning an agent on an instance with no reply filter, the filter defaults to {mode:"all", onDm:true} so messages are dispatched instead of silently dropped (omni#443).').option("--agent-provider <id>", 'Agent provider ID (use "null" to clear)').option("--agent <id>", 'Agent ID (use "null" to clear)').option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v) => Number.parseInt(v, 10)).option("--agent-stream-mode", "Enable streaming responses").option("--no-agent-stream-mode", "Disable streaming responses").option("--agent-session-strategy <strategy>", "Session strategy: per_user, per_chat, per_user_per_chat").option("--agent-prefix-sender-name", "Prefix messages with sender name").option("--no-agent-prefix-sender-name", "Disable sender name prefix").option("--agent-wait-for-media", "Wait for media processing before dispatch").option("--no-agent-wait-for-media", "Dispatch immediately without waiting for media").option("--agent-send-media-path", "Include file path in formatted media text").option("--no-agent-send-media-path", "Exclude file path from formatted media text").option("--agent-send-media-path-types <types>", "Content types that receive file path (comma-separated: image,video,document)").option("--reply-filter-mode <mode>", "Reply filter: all or filtered").option("--reply-on-dm", "Reply to DMs (requires --reply-filter-mode filtered)").option("--no-reply-on-dm", "Ignore DMs").option("--reply-on-mention", "Reply when @mentioned").option("--no-reply-on-mention", "Ignore @mentions").option("--reply-on-reply", "Reply when message is reply to bot").option("--no-reply-on-reply", "Ignore replies").option("--reply-on-name", "Reply when bot name appears in text").option("--no-reply-on-name", "Ignore name matches").option("--reply-name-patterns <patterns>", "Custom name patterns (comma-separated)").option("--clear-reply-filter", "Remove reply filter (set to null)").option("--enable-auto-split", "Split responses on double newlines").option("--no-enable-auto-split", "Disable auto-split").option("--message-format-mode <mode>", "Format mode: convert or passthrough").option("--debounce-mode <mode>", "Debounce mode: disabled, fixed, randomized, or presence").option("--debounce-min <ms>", "Minimum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--no-debounce-restart-on-typing", "Do not restart debounce on typing").option("--debounce-group <ms>", 'Group chat debounce in ms (use "null" to inherit)', (v) => v === "null" ? null : Number.parseInt(v, 10)).option("--debounce-max-wait <ms>", 'Presence-mode hard cap in ms (use "null" to inherit)', (v) => v === "null" ? null : Number.parseInt(v, 10)).option("--split-delay-mode <mode>", "Split delay mode: disabled, fixed, or randomized").option("--split-delay-fixed <ms>", "Fixed delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--agent-gate", "Enable LLM response gate").option("--no-agent-gate", "Disable LLM response gate").option("--agent-gate-model <model>", 'Model for response gate (use "null" for default)').option("--agent-gate-prompt <prompt>", 'Custom gate prompt (use "null" for default)').option("--tts-voice <id>", 'ElevenLabs voice ID (use "null" to clear)').option("--tts-model <id>", 'ElevenLabs model ID (use "null" to clear)').option("--read-receipts <mode>", "Read receipts mode: on, off, or exclude-self").option("--access-mode <mode>", "Access mode: disabled, blocklist, or allowlist").option("--reaction-ack <mode>", "Reaction ack mode (on|off)").option("--reaction-ack-emoji <json>", "Per-channel emoji map as JSON").option("--ack-timeout <ms>", "Ack timeout in milliseconds", (v) => Number.parseInt(v, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v) => Number.parseInt(v, 10)).option("--token <token>", "Generic bot token (auto-resolves to channel-specific field)").option("--telegram-token <token>", 'Telegram bot token (use "null" to clear)').option("--discord-token <token>", 'Discord bot token (use "null" to clear)').option("--slack-bot-token <token>", 'Slack bot token (use "null" to clear)').option("--slack-app-token <token>", 'Slack app token (use "null" to clear)').option("--twilio-account-sid <sid>", 'Twilio Account SID (use "null" to clear)').option("--twilio-auth-token <token>", 'Twilio Auth Token (use "null" to clear)').option("--twilio-from <address>", 'Twilio WhatsApp sender address (use "null" to clear)').option("--twilio-messaging-service-sid <sid>", 'Twilio Messaging Service SID (use "null" to clear)').option("--twilio-status-callback-url <url>", 'Twilio outbound status callback URL (use "null" to clear)').option("--twilio-webhook-url <url>", 'Public Twilio webhook URL for signature validation (use "null" to clear)').option("--twilio-validate-signature", "Validate X-Twilio-Signature on webhooks").option("--no-twilio-validate-signature", "Disable X-Twilio-Signature validation").option("--trigger-events <events>", 'Trigger events (comma-separated, use "null" to clear)').option("--profile-name <name>", "Update WhatsApp display name (push name)").option("--bridge-tmux-session <name>", 'Tmux session name the genie bridge spawns into for this instance (propagated as GENIE_TMUX_SESSION via NATS). Use "null" to clear.').option("--require-genie-signature", "Require a verified X-Genie-Signature on requests targeting this instance. Bearer-only requests will be rejected with 401.").option("--no-require-genie-signature", "Allow bearer-only requests targeting this instance (default).").action(async (rawId, options) => {
|
|
132955
|
+
instances.command("update <id>").description("Update an instance (supports all API fields)").option("--name <name>", "Instance name").option("--is-default", "Set as default instance for channel").option("--no-is-default", "Unset as default instance for channel").option("--agent-fk-id <uuid>", 'Agent FK UUID (references agents table, use "null" to clear). When assigning an agent on an instance with no reply filter, the filter defaults to {mode:"all", onDm:true} so messages are dispatched instead of silently dropped (omni#443).').option("--agent-provider <id>", 'Agent provider ID (use "null" to clear)').option("--agent <id>", 'Agent ID (use "null" to clear)').option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v) => Number.parseInt(v, 10)).option("--agent-stream-mode", "Enable streaming responses").option("--no-agent-stream-mode", "Disable streaming responses").option("--agent-session-strategy <strategy>", "Session strategy: per_user, per_chat, per_user_per_chat").option("--agent-prefix-sender-name", "Prefix messages with sender name").option("--no-agent-prefix-sender-name", "Disable sender name prefix").option("--agent-wait-for-media", "Wait for media processing before dispatch").option("--no-agent-wait-for-media", "Dispatch immediately without waiting for media").option("--agent-send-media-path", "Include file path in formatted media text").option("--no-agent-send-media-path", "Exclude file path from formatted media text").option("--agent-send-media-path-types <types>", "Content types that receive file path (comma-separated: image,video,document)").option("--reply-filter-mode <mode>", "Reply filter: all or filtered").option("--reply-on-dm", "Reply to DMs (requires --reply-filter-mode filtered)").option("--no-reply-on-dm", "Ignore DMs").option("--reply-on-mention", "Reply when @mentioned").option("--no-reply-on-mention", "Ignore @mentions").option("--reply-on-reply", "Reply when message is reply to bot").option("--no-reply-on-reply", "Ignore replies").option("--reply-on-name", "Reply when bot name appears in text").option("--no-reply-on-name", "Ignore name matches").option("--reply-name-patterns <patterns>", "Custom name patterns (comma-separated)").option("--clear-reply-filter", "Remove reply filter (set to null)").option("--enable-auto-split", "Split responses on double newlines").option("--no-enable-auto-split", "Disable auto-split").option("--message-format-mode <mode>", "Format mode: convert or passthrough").option("--debounce-mode <mode>", "Debounce mode: disabled, fixed, randomized, or presence").option("--debounce-min <ms>", "Minimum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v) => Number.parseInt(v, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--no-debounce-restart-on-typing", "Do not restart debounce on typing").option("--debounce-group <ms>", 'Group chat debounce in ms (use "null" to inherit)', (v) => v === "null" ? null : Number.parseInt(v, 10)).option("--debounce-max-wait <ms>", 'Presence-mode hard cap in ms (use "null" to inherit)', (v) => v === "null" ? null : Number.parseInt(v, 10)).option("--split-delay-mode <mode>", "Split delay mode: disabled, fixed, or randomized").option("--split-delay-fixed <ms>", "Fixed delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v) => Number.parseInt(v, 10)).option("--agent-gate", "Enable LLM response gate").option("--no-agent-gate", "Disable LLM response gate").option("--agent-gate-model <model>", 'Model for response gate (use "null" for default)').option("--agent-gate-prompt <prompt>", 'Custom gate prompt (use "null" for default)').option("--tts-voice <id>", 'ElevenLabs voice ID (use "null" to clear)').option("--tts-model <id>", 'ElevenLabs model ID (use "null" to clear)').option("--read-receipts <mode>", "Read receipts mode: on, off, or exclude-self").option("--access-mode <mode>", "Access mode: disabled, blocklist, or allowlist").option("--reaction-ack <mode>", "Reaction ack mode (on|off)").option("--reaction-ack-emoji <json>", "Per-channel emoji map as JSON").option("--ack-timeout <ms>", "Ack timeout in milliseconds", (v) => Number.parseInt(v, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v) => Number.parseInt(v, 10)).option("--token <token>", "Generic bot token (auto-resolves to channel-specific field)").option("--telegram-token <token>", 'Telegram bot token (use "null" to clear)').option("--discord-token <token>", 'Discord bot token (use "null" to clear)').option("--slack-bot-token <token>", 'Slack bot token (use "null" to clear)').option("--slack-app-token <token>", 'Slack app token (use "null" to clear)').option("--twilio-account-sid <sid>", 'Twilio Account SID (use "null" to clear)').option("--twilio-auth-token <token>", 'Twilio Auth Token (use "null" to clear)').option("--twilio-from <address>", 'Twilio WhatsApp sender address (use "null" to clear)').option("--twilio-messaging-service-sid <sid>", 'Twilio Messaging Service SID (use "null" to clear)').option("--twilio-status-callback-url <url>", 'Twilio outbound status callback URL (use "null" to clear)').option("--twilio-webhook-url <url>", 'Public Twilio webhook URL for signature validation (use "null" to clear)').option("--twilio-validate-signature", "Validate X-Twilio-Signature on webhooks").option("--no-twilio-validate-signature", "Disable X-Twilio-Signature validation").option("--trigger-events <events>", 'Trigger events (comma-separated, use "null" to clear)').option("--profile-name <name>", "Update WhatsApp display name (push name)").option("--bridge-tmux-session <name>", 'Tmux session name the genie bridge spawns into for this instance (propagated as GENIE_TMUX_SESSION via NATS). Use "null" to clear.').option("--require-genie-signature", "Require a verified X-Genie-Signature on requests targeting this instance. Bearer-only requests will be rejected with 401.").option("--no-require-genie-signature", "Allow bearer-only requests targeting this instance (default).").option("--allow-first-party", "Process (do not drop) inbound messages whose sender matches another active instance owner. Lets this instance reply to messages from your own personal number.").option("--no-allow-first-party", "Drop inbound messages whose sender matches another active instance owner (default, loop-protection).").action(async (rawId, options) => {
|
|
132954
132956
|
const client = getClient();
|
|
132955
132957
|
try {
|
|
132956
132958
|
const id = await resolveInstanceId(rawId);
|
package/dist/server/index.js
CHANGED
|
@@ -225489,7 +225489,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
225489
225489
|
var require_package7 = __commonJS((exports, module) => {
|
|
225490
225490
|
module.exports = {
|
|
225491
225491
|
name: "@omni/api",
|
|
225492
|
-
version: "2.260704.
|
|
225492
|
+
version: "2.260704.2",
|
|
225493
225493
|
type: "module",
|
|
225494
225494
|
exports: {
|
|
225495
225495
|
".": {
|
|
@@ -278057,6 +278057,7 @@ var init_schema2 = __esm(() => {
|
|
|
278057
278057
|
followUpConfig: jsonb("follow_up_config").$type(),
|
|
278058
278058
|
bridgeTmuxSession: text("bridge_tmux_session"),
|
|
278059
278059
|
requireGenieSignature: boolean("require_genie_signature").notNull().default(false),
|
|
278060
|
+
allowFirstParty: boolean("allow_first_party").notNull().default(false),
|
|
278060
278061
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
278061
278062
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
278062
278063
|
}, (table3) => ({
|
|
@@ -316254,7 +316255,7 @@ var init_audio2 = __esm(() => {
|
|
|
316254
316255
|
}
|
|
316255
316256
|
if (provider === "gemini") {
|
|
316256
316257
|
return [
|
|
316257
|
-
() => this.transcribeWithGemini(language, options,
|
|
316258
|
+
() => this.transcribeWithGemini(language, options, this.resolveGeminiAudioModel(options?.model), getNormalizedAudio),
|
|
316258
316259
|
() => this.transcribeWithOpenAiAudioChat(language, options, OPENAI_AUDIO_CHAT_MODEL, getNormalizedAudio),
|
|
316259
316260
|
() => this.transcribeWithGroq(language, getNormalizedAudio)
|
|
316260
316261
|
];
|
|
@@ -316264,6 +316265,11 @@ var init_audio2 = __esm(() => {
|
|
|
316264
316265
|
() => this.transcribeWithOpenAiTranscriptions(language, options, preferredModel ?? OPENAI_TRANSCRIBE_MODEL, getNormalizedAudio)
|
|
316265
316266
|
];
|
|
316266
316267
|
}
|
|
316268
|
+
resolveGeminiAudioModel(optionModel) {
|
|
316269
|
+
if (optionModel?.startsWith("gemini"))
|
|
316270
|
+
return optionModel;
|
|
316271
|
+
return this.config.geminiAudioModel ?? GEMINI_AUDIO_MODEL;
|
|
316272
|
+
}
|
|
316267
316273
|
async transcribeWithOpenAiAudioChat(language, options, model, getNormalizedAudio) {
|
|
316268
316274
|
if (!this.config.openaiApiKey) {
|
|
316269
316275
|
return this.createFailedResult("OpenAI client not configured (missing API key)", "openai", model);
|
|
@@ -333107,6 +333113,7 @@ function createMediaProcessingService(config4) {
|
|
|
333107
333113
|
geminiApiKey: config4?.geminiApiKey ?? process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY,
|
|
333108
333114
|
audioProvider: config4?.audioProvider ?? process.env.STT_PROVIDER,
|
|
333109
333115
|
audioModel: config4?.audioModel ?? process.env.OPENAI_STT_MODEL,
|
|
333116
|
+
geminiAudioModel: config4?.geminiAudioModel ?? process.env.GEMINI_STT_MODEL,
|
|
333110
333117
|
audioPrompt: config4?.audioPrompt,
|
|
333111
333118
|
audioGlossary: config4?.audioGlossary,
|
|
333112
333119
|
defaultLanguage: config4?.defaultLanguage ?? process.env.DEFAULT_LANGUAGE ?? "pt",
|
|
@@ -333433,13 +333440,23 @@ class BatchJobService {
|
|
|
333433
333440
|
async createMediaService() {
|
|
333434
333441
|
let config4;
|
|
333435
333442
|
if (this.settings) {
|
|
333436
|
-
const [
|
|
333443
|
+
const [
|
|
333444
|
+
groqApiKey,
|
|
333445
|
+
openaiApiKey,
|
|
333446
|
+
geminiApiKey,
|
|
333447
|
+
defaultLanguage,
|
|
333448
|
+
audioProvider,
|
|
333449
|
+
audioModel,
|
|
333450
|
+
geminiAudioModel,
|
|
333451
|
+
audioPrompt
|
|
333452
|
+
] = await Promise.all([
|
|
333437
333453
|
this.settings.getSecret("groq.api_key", "GROQ_API_KEY"),
|
|
333438
333454
|
this.settings.getSecret("openai.api_key", "OPENAI_API_KEY"),
|
|
333439
333455
|
this.settings.getSecret("gemini.api_key", "GEMINI_API_KEY"),
|
|
333440
333456
|
this.settings.getString("media.default_language", "DEFAULT_LANGUAGE", "pt"),
|
|
333441
333457
|
this.settings.getString("stt.provider", "STT_PROVIDER", "openai"),
|
|
333442
333458
|
this.settings.getString("stt.openai.model", "OPENAI_STT_MODEL", "gpt-audio-mini"),
|
|
333459
|
+
this.settings.getString("stt.gemini.model", "GEMINI_STT_MODEL", GEMINI_AUDIO_MODEL),
|
|
333443
333460
|
this.settings.getString("prompt.audio_transcription")
|
|
333444
333461
|
]);
|
|
333445
333462
|
config4 = {
|
|
@@ -333449,6 +333466,7 @@ class BatchJobService {
|
|
|
333449
333466
|
defaultLanguage: defaultLanguage ?? "pt",
|
|
333450
333467
|
audioProvider: audioProvider ?? "openai",
|
|
333451
333468
|
audioModel: audioModel ?? "gpt-audio-mini",
|
|
333469
|
+
geminiAudioModel: geminiAudioModel ?? GEMINI_AUDIO_MODEL,
|
|
333452
333470
|
audioPrompt: audioPrompt ?? undefined
|
|
333453
333471
|
};
|
|
333454
333472
|
}
|
|
@@ -341135,7 +341153,7 @@ async function shouldProcessMessage3(agentRunner, accessService, chatsService, m
|
|
|
341135
341153
|
return null;
|
|
341136
341154
|
}
|
|
341137
341155
|
const activeOwnerIdentifiers = await listActiveOwnerIdentifiers(db2);
|
|
341138
|
-
if (isFirstPartyInstanceSender(payload, instance4.ownerIdentifier, activeOwnerIdentifiers)) {
|
|
341156
|
+
if (!instance4.allowFirstParty && isFirstPartyInstanceSender(payload, instance4.ownerIdentifier, activeOwnerIdentifiers)) {
|
|
341139
341157
|
log100.info("Skipping first-party cross-instance message", {
|
|
341140
341158
|
instanceId: instance4.id
|
|
341141
341159
|
});
|
|
@@ -354041,7 +354059,8 @@ var init_instances3 = __esm(() => {
|
|
|
354041
354059
|
ackTimeoutMs: exports_external.number().int().min(0).max(120000).default(30000).describe("Ack timeout in milliseconds (max 120000)"),
|
|
354042
354060
|
agentStalledTimeoutMs: exports_external.number().int().min(0).default(600000).describe("Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)"),
|
|
354043
354061
|
bridgeTmuxSession: exports_external.string().min(1).max(64).regex(/^[a-zA-Z0-9_.-]+$/, "Tmux session name must be alphanumeric with `_`, `-`, or `.` (no `/`, `:`, or whitespace).").optional().nullable().describe("Tmux session name the genie bridge will spawn into for this instance. Propagated via NATS env as GENIE_TMUX_SESSION. Null clears the override (genie falls back to its agent-level or name-based default). Max 64 chars, `[A-Za-z0-9_.-]` only."),
|
|
354044
|
-
requireGenieSignature: exports_external.boolean().default(false).describe("When true, requests targeting this instance MUST carry a verified X-Genie-Signature. Bearer-only requests get 401. Default: false (additive rollout).")
|
|
354062
|
+
requireGenieSignature: exports_external.boolean().default(false).describe("When true, requests targeting this instance MUST carry a verified X-Genie-Signature. Bearer-only requests get 401. Default: false (additive rollout)."),
|
|
354063
|
+
allowFirstParty: exports_external.boolean().default(false).describe(`When true, this instance processes (does not drop) inbound messages whose sender matches another active instance owner. Lets an "assistant" instance reply to messages from the operator's own personal number. Default: false (loop-protection drop).`)
|
|
354045
354064
|
});
|
|
354046
354065
|
updateInstanceSchema = createInstanceSchema.partial().extend({
|
|
354047
354066
|
agentId: exports_external.string().uuid().nullable().optional(),
|
|
@@ -354073,7 +354092,8 @@ var init_instances3 = __esm(() => {
|
|
|
354073
354092
|
messageSplitDelayMinMs: exports_external.number().int().min(0).optional(),
|
|
354074
354093
|
messageSplitDelayMaxMs: exports_external.number().int().min(0).optional(),
|
|
354075
354094
|
twilioValidateSignature: exports_external.boolean().optional(),
|
|
354076
|
-
requireGenieSignature: exports_external.boolean().optional()
|
|
354095
|
+
requireGenieSignature: exports_external.boolean().optional(),
|
|
354096
|
+
allowFirstParty: exports_external.boolean().optional()
|
|
354077
354097
|
});
|
|
354078
354098
|
DEFAULT_AGENT_REPLY_FILTER = {
|
|
354079
354099
|
mode: "all",
|
|
@@ -361060,6 +361080,7 @@ async function setupMediaProcessor(eventBus, db2, services) {
|
|
|
361060
361080
|
defaultLanguage,
|
|
361061
361081
|
audioProvider,
|
|
361062
361082
|
audioModel,
|
|
361083
|
+
geminiAudioModel,
|
|
361063
361084
|
audioPrompt,
|
|
361064
361085
|
imagePrompt,
|
|
361065
361086
|
videoPrompt,
|
|
@@ -361071,6 +361092,7 @@ async function setupMediaProcessor(eventBus, db2, services) {
|
|
|
361071
361092
|
services.settings.getString("media.default_language", "DEFAULT_LANGUAGE", "pt"),
|
|
361072
361093
|
services.settings.getString("stt.provider", "STT_PROVIDER", "openai"),
|
|
361073
361094
|
services.settings.getString("stt.openai.model", "OPENAI_STT_MODEL", "gpt-audio-mini"),
|
|
361095
|
+
services.settings.getString("stt.gemini.model", "GEMINI_STT_MODEL", GEMINI_AUDIO_MODEL),
|
|
361074
361096
|
services.settings.getString("prompt.audio_transcription"),
|
|
361075
361097
|
services.settings.getString("prompt.image_description"),
|
|
361076
361098
|
services.settings.getString("prompt.video_description"),
|
|
@@ -361083,6 +361105,7 @@ async function setupMediaProcessor(eventBus, db2, services) {
|
|
|
361083
361105
|
defaultLanguage,
|
|
361084
361106
|
audioProvider: audioProvider ?? "openai",
|
|
361085
361107
|
audioModel: audioModel ?? "gpt-audio-mini",
|
|
361108
|
+
geminiAudioModel: geminiAudioModel ?? GEMINI_AUDIO_MODEL,
|
|
361086
361109
|
audioPrompt: audioPrompt ?? undefined
|
|
361087
361110
|
});
|
|
361088
361111
|
const mediaStorage2 = new MediaStorageService(db2);
|