@automagik/omni 2.260916.4 → 2.260916.6

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,17 @@
1
+ -- WhatsApp history-sync settings on instances (issue #1211).
2
+ --
3
+ -- #1126 made the pairing identity configurable, but only via syncFullHistory,
4
+ -- which had no column, so it was unreachable. Persist both per instance:
5
+ --
6
+ -- instances.history_identity 'desktop' (macOS Desktop + group history) | 'web'
7
+ -- instances.sync_full_history full history push on connect (default false, #70)
8
+ --
9
+ -- Hand-written following the 0043/0044/0052 precedent (additive, idempotent).
10
+ -- No explicit BEGIN/COMMIT — the boot migrator executes this file on a pooled
11
+ -- postgres-js connection, which rejects raw transaction control.
12
+
13
+ ALTER TABLE "instances"
14
+ ADD COLUMN IF NOT EXISTS "history_identity" varchar(10) DEFAULT 'desktop' NOT NULL;
15
+
16
+ ALTER TABLE "instances"
17
+ ADD COLUMN IF NOT EXISTS "sync_full_history" boolean DEFAULT false NOT NULL;
@@ -533,6 +533,13 @@
533
533
  "when": 1787500000000,
534
534
  "tag": "0075_automation_logs_queue_wait",
535
535
  "breakpoints": true
536
+ },
537
+ {
538
+ "idx": 76,
539
+ "version": "7",
540
+ "when": 1787600000000,
541
+ "tag": "0076_instances_history_identity",
542
+ "breakpoints": true
536
543
  }
537
544
  ]
538
545
  }
@@ -32,6 +32,8 @@ declare function maskSecretFields(body: Record<string, unknown>): Record<string,
32
32
  * `webhooks --signature-secret-*`). Undefined when none is given.
33
33
  */
34
34
  declare function resolveSlackUserToken(opts: Record<string, unknown>, readStdin?: () => Promise<string>): Promise<string | undefined>;
35
+ /** Build instance body from all CLI options */
36
+ export declare function buildInstanceBody(opts: Record<string, unknown>): Record<string, unknown>;
35
37
  export declare function createInstancesCommand(): Command;
36
38
  export declare const __testables: {
37
39
  maskSecret: typeof maskSecret;
@@ -1 +1 @@
1
- {"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../src/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuLpC,kFAAkF;AAClF,iBAAS,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAG1C;AAED,+FAA+F;AAC/F,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOhF;AAcD;;;;GAIG;AACH,iBAAe,qBAAqB,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,GAAE,MAAM,OAAO,CAAC,MAAM,CAAiB,GAC/C,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAsB7B;AA6CD,wBAAgB,sBAAsB,IAAI,OAAO,CAu5ChD;AAED,eAAO,MAAM,WAAW;;;;CAA0D,CAAC"}
1
+ {"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../src/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAyL5C,kFAAkF;AAClF,iBAAS,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAG1C;AAED,+FAA+F;AAC/F,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOhF;AAcD;;;;GAIG;AACH,iBAAe,qBAAqB,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,GAAE,MAAM,OAAO,CAAC,MAAM,CAAiB,GAC/C,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAsB7B;AAED,+CAA+C;AAC/C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYxF;AA8BD,wBAAgB,sBAAsB,IAAI,OAAO,CAu6ChD;AAED,eAAO,MAAM,WAAW;;;;CAA0D,CAAC"}
package/dist/index.js CHANGED
@@ -82419,6 +82419,8 @@ var init_schema2 = __esm(() => {
82419
82419
  agentSendMediaPathTypes: text("agent_send_media_path_types").array(),
82420
82420
  readReceipts: varchar("read_receipts", { length: 20 }).notNull().default("on").$type(),
82421
82421
  markOnlineOnConnect: boolean2("mark_online_on_connect").notNull().default(true),
82422
+ historyIdentity: varchar("history_identity", { length: 10 }).notNull().default("desktop").$type(),
82423
+ syncFullHistory: boolean2("sync_full_history").notNull().default(false),
82422
82424
  groupHistorySize: integer("group_history_size").notNull().default(50),
82423
82425
  lastMessageAt: timestamp("last_message_at", { withTimezone: true }),
82424
82426
  replayEnabled: boolean2("replay_enabled").notNull().default(true),
@@ -137420,7 +137422,7 @@ import { fileURLToPath } from "url";
137420
137422
  // package.json
137421
137423
  var package_default = {
137422
137424
  name: "@automagik/omni",
137423
- version: "2.260916.4",
137425
+ version: "2.260916.6",
137424
137426
  repository: {
137425
137427
  type: "git",
137426
137428
  url: "git+https://github.com/automagik-dev/omni.git",
@@ -146052,6 +146054,8 @@ function applyMiscFields(body, opts) {
146052
146054
  setVal(body, "ttsVoiceId", opts.ttsVoice);
146053
146055
  setVal(body, "ttsModelId", opts.ttsModel);
146054
146056
  setVal(body, "readReceipts", opts.readReceipts);
146057
+ setVal(body, "historyIdentity", opts.historyIdentity);
146058
+ setBool(body, "syncFullHistory", opts.syncFullHistory);
146055
146059
  setVal(body, "accessMode", opts.accessMode);
146056
146060
  setVal(body, "token", opts.token);
146057
146061
  setVal(body, "telegramBotToken", opts.telegramToken);
@@ -146252,7 +146256,7 @@ function createInstancesCommand() {
146252
146256
  error(`Failed to get instance: ${message2}`, undefined, 3);
146253
146257
  }
146254
146258
  });
146255
- 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 <id>", "Agent ID").option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v4) => Number.parseInt(v4, 10)).option("--agent-error-message <text>", 'Customer-facing reply when agent dispatch fails; repeat the flag for multiple variants (one is picked at random per failure). Use "null" to clear; falls back to OMNI_AGENT_DISPATCH_ERROR_MESSAGE env, then built-in default', (value, previous = []) => [...previous, value]).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", (v4) => Number.parseInt(v4, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v4) => Number.parseInt(v4, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--debounce-group <ms>", "Group chat debounce in ms", (v4) => Number.parseInt(v4, 10)).option("--debounce-max-wait <ms>", "Presence-mode hard cap in ms (flush even under continuous typing)", (v4) => Number.parseInt(v4, 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", (v4) => Number.parseInt(v4, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v4) => Number.parseInt(v4, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v4) => Number.parseInt(v4, 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", (v4) => Number.parseInt(v4, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v4) => Number.parseInt(v4, 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("--slack-user-token <token>", "Slack user token (xoxp-...), required for --slack-auth-mode user").option("--slack-user-token-env <VAR>", "Read the Slack user token from environment variable VAR").option("--slack-user-token-stdin", "Read the Slack user token from stdin").option("--slack-auth-mode <mode>", "Slack identity for outbound actions: bot (default) or user").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-handoff-options <json>", "Gupshup HANDOFF routing defaults and customerFields template (JSON object)").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").option("--force", "Proceed even if another active Slack instance uses the same app token").action(async (options) => {
146259
+ 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 <id>", "Agent ID").option("--agent-type <type>", "Agent type: agent, team, or workflow").option("--agent-timeout <seconds>", "Agent timeout in seconds", (v4) => Number.parseInt(v4, 10)).option("--agent-error-message <text>", 'Customer-facing reply when agent dispatch fails; repeat the flag for multiple variants (one is picked at random per failure). Use "null" to clear; falls back to OMNI_AGENT_DISPATCH_ERROR_MESSAGE env, then built-in default', (value, previous = []) => [...previous, value]).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", (v4) => Number.parseInt(v4, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v4) => Number.parseInt(v4, 10)).option("--debounce-restart-on-typing", "Restart debounce timer on typing").option("--debounce-group <ms>", "Group chat debounce in ms", (v4) => Number.parseInt(v4, 10)).option("--debounce-max-wait <ms>", "Presence-mode hard cap in ms (flush even under continuous typing)", (v4) => Number.parseInt(v4, 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", (v4) => Number.parseInt(v4, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v4) => Number.parseInt(v4, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v4) => Number.parseInt(v4, 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").addOption(new Option("--history-identity <identity>", "WhatsApp pairing identity: desktop (macOS Desktop + group history, fuller sync) or web (Ubuntu/Chrome). Applies on next pairing (default: desktop)").choices(["desktop", "web"])).option("--sync-full-history", "WhatsApp: request full message history on connect (default: off)").option("--no-sync-full-history", "WhatsApp: do not request full message history on connect").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", (v4) => Number.parseInt(v4, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v4) => Number.parseInt(v4, 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("--slack-user-token <token>", "Slack user token (xoxp-...), required for --slack-auth-mode user").option("--slack-user-token-env <VAR>", "Read the Slack user token from environment variable VAR").option("--slack-user-token-stdin", "Read the Slack user token from stdin").option("--slack-auth-mode <mode>", "Slack identity for outbound actions: bot (default) or user").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-handoff-options <json>", "Gupshup HANDOFF routing defaults and customerFields template (JSON object)").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").option("--force", "Proceed even if another active Slack instance uses the same app token").action(async (options) => {
146256
146260
  const channel2 = options.channel;
146257
146261
  if (!VALID_CHANNELS2.includes(channel2)) {
146258
146262
  error(`Invalid channel: ${channel2}`, { validChannels: VALID_CHANNELS2 });
@@ -146605,7 +146609,7 @@ function createInstancesCommand() {
146605
146609
  throw new Error(err2?.error?.message ?? `HTTP ${response.status}`);
146606
146610
  }
146607
146611
  }
146608
- 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>", "Removed: set the provider on the agent (omni agents update <agentId> --agent-provider)").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", (v4) => Number.parseInt(v4, 10)).option("--agent-error-message <text>", 'Customer-facing reply when agent dispatch fails; repeat the flag for multiple variants (one is picked at random per failure). Use "null" to clear; falls back to OMNI_AGENT_DISPATCH_ERROR_MESSAGE env, then built-in default', (value, previous = []) => [...previous, value]).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", (v4) => Number.parseInt(v4, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v4) => Number.parseInt(v4, 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)', (v4) => v4 === "null" ? null : Number.parseInt(v4, 10)).option("--debounce-max-wait <ms>", 'Presence-mode hard cap in ms (use "null" to inherit)', (v4) => v4 === "null" ? null : Number.parseInt(v4, 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", (v4) => Number.parseInt(v4, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v4) => Number.parseInt(v4, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v4) => Number.parseInt(v4, 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", (v4) => Number.parseInt(v4, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v4) => Number.parseInt(v4, 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("--slack-user-token <token>", 'Slack user token xoxp-... (use "null" to clear)').option("--slack-user-token-env <VAR>", "Read the Slack user token from environment variable VAR").option("--slack-user-token-stdin", "Read the Slack user token from stdin").option("--slack-auth-mode <mode>", 'Slack identity: bot or user (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("--gupshup-handoff-options <json>", 'Gupshup HANDOFF routing defaults and customerFields template (JSON object, use "null" to clear). The plugin reads it at connect: restart the instance for the change to take effect').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).").option("--force", "Proceed even if another active Slack instance uses the same app token").action(async (rawId, options) => {
146612
+ 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>", "Removed: set the provider on the agent (omni agents update <agentId> --agent-provider)").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", (v4) => Number.parseInt(v4, 10)).option("--agent-error-message <text>", 'Customer-facing reply when agent dispatch fails; repeat the flag for multiple variants (one is picked at random per failure). Use "null" to clear; falls back to OMNI_AGENT_DISPATCH_ERROR_MESSAGE env, then built-in default', (value, previous = []) => [...previous, value]).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", (v4) => Number.parseInt(v4, 10)).option("--debounce-max <ms>", "Maximum debounce delay in ms", (v4) => Number.parseInt(v4, 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)', (v4) => v4 === "null" ? null : Number.parseInt(v4, 10)).option("--debounce-max-wait <ms>", 'Presence-mode hard cap in ms (use "null" to inherit)', (v4) => v4 === "null" ? null : Number.parseInt(v4, 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", (v4) => Number.parseInt(v4, 10)).option("--split-delay-min <ms>", "Minimum delay between split chunks in ms", (v4) => Number.parseInt(v4, 10)).option("--split-delay-max <ms>", "Maximum delay between split chunks in ms", (v4) => Number.parseInt(v4, 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").addOption(new Option("--history-identity <identity>", "WhatsApp pairing identity: desktop (macOS Desktop + group history, fuller sync) or web (Ubuntu/Chrome). Applies on next pairing (default: desktop)").choices(["desktop", "web"])).option("--sync-full-history", "WhatsApp: request full message history on connect (default: off)").option("--no-sync-full-history", "WhatsApp: do not request full message history on connect").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", (v4) => Number.parseInt(v4, 10)).option("--agent-stalled-timeout-ms <ms>", "Idle threshold in ms before the internal turn.stalled event fires (no channel message is ever sent)", (v4) => Number.parseInt(v4, 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("--slack-user-token <token>", 'Slack user token xoxp-... (use "null" to clear)').option("--slack-user-token-env <VAR>", "Read the Slack user token from environment variable VAR").option("--slack-user-token-stdin", "Read the Slack user token from stdin").option("--slack-auth-mode <mode>", 'Slack identity: bot or user (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("--gupshup-handoff-options <json>", 'Gupshup HANDOFF routing defaults and customerFields template (JSON object, use "null" to clear). The plugin reads it at connect: restart the instance for the change to take effect').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).").option("--force", "Proceed even if another active Slack instance uses the same app token").action(async (rawId, options) => {
146609
146613
  const client = getClient();
146610
146614
  if (options.agentProvider !== undefined) {
146611
146615
  error("instances update no longer accepts --agent-provider: the provider belongs to the agent. Use `omni agents update <agentId> --agent-provider <providerId>`.");
@@ -172904,6 +172904,8 @@ var init_schema2 = __esm(() => {
172904
172904
  agentSendMediaPathTypes: text("agent_send_media_path_types").array(),
172905
172905
  readReceipts: varchar("read_receipts", { length: 20 }).notNull().default("on").$type(),
172906
172906
  markOnlineOnConnect: boolean2("mark_online_on_connect").notNull().default(true),
172907
+ historyIdentity: varchar("history_identity", { length: 10 }).notNull().default("desktop").$type(),
172908
+ syncFullHistory: boolean2("sync_full_history").notNull().default(false),
172907
172909
  groupHistorySize: integer("group_history_size").notNull().default(50),
172908
172910
  lastMessageAt: timestamp("last_message_at", { withTimezone: true }),
172909
172911
  replayEnabled: boolean2("replay_enabled").notNull().default(true),
@@ -251111,7 +251113,7 @@ var init_sentry_scrub = __esm(() => {
251111
251113
  var require_package7 = __commonJS((exports, module) => {
251112
251114
  module.exports = {
251113
251115
  name: "@omni/api",
251114
- version: "2.260916.4",
251116
+ version: "2.260916.6",
251115
251117
  private: true,
251116
251118
  type: "module",
251117
251119
  exports: {
@@ -381600,6 +381602,10 @@ var init_instances2 = __esm(() => {
381600
381602
  isDefault: exports_external.boolean().openapi({ description: "Whether this is the default instance for channel" }),
381601
381603
  profileName: exports_external.string().nullable().openapi({ description: "Connected profile name" }),
381602
381604
  profilePicUrl: exports_external.string().nullable().openapi({ description: "Profile picture URL" }),
381605
+ historyIdentity: exports_external.enum(["desktop", "web"]).openapi({
381606
+ description: "WhatsApp (Baileys) pairing identity: 'desktop' (macOS Desktop + group history) or 'web' (Ubuntu/Chrome)"
381607
+ }),
381608
+ syncFullHistory: exports_external.boolean().openapi({ description: "WhatsApp (Baileys): request full history on connect" }),
381603
381609
  ownerIdentifier: exports_external.string().nullable().openapi({ description: "Owner identifier" }),
381604
381610
  agentId: exports_external.string().uuid().nullable().optional().openapi({ description: "Agent UUID (agents table)" }),
381605
381611
  agentProviderId: exports_external.string().uuid().nullable().optional().openapi({ description: "Provider ID of the assigned agent (read-only; set via the agent)" }),
@@ -381656,6 +381662,10 @@ var init_instances2 = __esm(() => {
381656
381662
  agentStreamMode: exports_external.boolean().default(false).openapi({ description: "Enable streaming responses" }),
381657
381663
  isDefault: exports_external.boolean().default(false).openapi({ description: "Set as default instance for channel" }),
381658
381664
  token: exports_external.string().optional().openapi({ description: "Bot token for Discord instances" }),
381665
+ historyIdentity: exports_external.enum(["desktop", "web"]).default("desktop").openapi({
381666
+ description: `WhatsApp (Baileys) pairing identity (default: desktop). 'desktop' = macOS Desktop + group history, shows as "Mac OS" in Linked Devices; 'web' = Ubuntu/Chrome. Applies on next pairing`
381667
+ }),
381668
+ syncFullHistory: exports_external.boolean().default(false).openapi({ description: "WhatsApp (Baileys): request full message history on connect (default: false)" }),
381659
381669
  gupshupHandoffOptions: GupshupHandoffOptionsSchema.nullable().optional().openapi({
381660
381670
  description: "Gupshup HANDOFF routing defaults and customerFields template (gupshup instances only). Read by the plugin at connect: restart the instance after changing it. null clears it",
381661
381671
  example: {
@@ -389333,12 +389343,17 @@ function hydrateConnectionOptionsForInstance(plugin13, instance4, options) {
389333
389343
  if (instance4.discordPresence) {
389334
389344
  options.presence = instance4.discordPresence;
389335
389345
  }
389336
- if (instance4.channel === "whatsapp-baileys" && instance4.markOnlineOnConnect != null) {
389337
- options.whatsapp = {
389338
- ...options.whatsapp,
389339
- markOnlineOnConnect: instance4.markOnlineOnConnect
389340
- };
389341
- }
389346
+ applyWhatsAppBaileysOptions(options, instance4);
389347
+ }
389348
+ function applyWhatsAppBaileysOptions(options, instance4) {
389349
+ if (instance4.channel !== "whatsapp-baileys")
389350
+ return;
389351
+ options.whatsapp = {
389352
+ historyIdentity: instance4.historyIdentity,
389353
+ syncFullHistory: instance4.syncFullHistory,
389354
+ markOnlineOnConnect: instance4.markOnlineOnConnect,
389355
+ ...options.whatsapp
389356
+ };
389342
389357
  }
389343
389358
  function buildConnectPersistUpdates(instance4, body) {
389344
389359
  const updates = {
@@ -389582,6 +389597,8 @@ var init_instances3 = __esm(() => {
389582
389597
  readReceipts: exports_external.enum(["on", "off", "exclude-self"]).default("on").describe("Read receipt mode: on (default), off, or exclude-self (skip receipts for the instance own number)"),
389583
389598
  groupHistorySize: exports_external.number().int().min(0).max(200).default(50).describe("Number of context messages to include when dispatching to agent. Groups use the full value; DMs are capped at 20. (0 = disabled, max 200)"),
389584
389599
  markOnlineOnConnect: exports_external.boolean().default(true).describe("Mark the instance as online when connecting to WhatsApp (default: true). Set to false to preserve phone push notifications."),
389600
+ historyIdentity: exports_external.enum(["desktop", "web"]).default("desktop").describe(`WhatsApp (Baileys) pairing identity (default: desktop). 'desktop' = macOS Desktop + group history (fuller initial sync, shows as "Mac OS" in Linked Devices); 'web' = Ubuntu/Chrome. Applies on next pairing.`),
389601
+ syncFullHistory: exports_external.boolean().default(false).describe("WhatsApp (Baileys): request full message history on connect (default: false)"),
389585
389602
  reactionAck: exports_external.enum(["on", "off"]).default("off").describe("Reaction ack mode: on to send a reaction while agent processes"),
389586
389603
  reactionAckEmoji: exports_external.record(exports_external.string()).optional().nullable().describe('Per-channel emoji map for reaction ack (e.g. {"whatsapp":"\\u2705"})'),
389587
389604
  ackTimeoutMs: exports_external.number().int().min(0).max(120000).default(30000).describe("Ack timeout in milliseconds (max 120000)"),
@@ -389616,6 +389633,8 @@ var init_instances3 = __esm(() => {
389616
389633
  twilioWebhookUrl: exports_external.string().nullable().optional(),
389617
389634
  readReceipts: exports_external.enum(["on", "off", "exclude-self"]).optional(),
389618
389635
  markOnlineOnConnect: exports_external.boolean().optional(),
389636
+ historyIdentity: exports_external.enum(["desktop", "web"]).optional(),
389637
+ syncFullHistory: exports_external.boolean().optional(),
389619
389638
  groupHistorySize: exports_external.number().int().min(0).max(200).optional(),
389620
389639
  reactionAck: exports_external.enum(["on", "off"]).optional(),
389621
389640
  reactionAckEmoji: exports_external.record(exports_external.string()).nullable().optional(),
@@ -389628,7 +389647,7 @@ var init_instances3 = __esm(() => {
389628
389647
  twilioValidateSignature: exports_external.boolean().optional(),
389629
389648
  requireGenieSignature: exports_external.boolean().optional(),
389630
389649
  allowFirstParty: exports_external.boolean().optional()
389631
- });
389650
+ }).strict();
389632
389651
  DEFAULT_AGENT_REPLY_FILTER = {
389633
389652
  mode: "all",
389634
389653
  conditions: { onDm: true }
@@ -390026,7 +390045,7 @@ var init_instances3 = __esm(() => {
390026
390045
  ascFlowHandoffServico: exports_external.number().int().optional().describe("cod_servico handed to /transferirHumano (service mode)"),
390027
390046
  force: exports_external.boolean().optional().describe("Override the shared-Slack-app-token refusal (Socket Mode would split events between instances)"),
390028
390047
  whatsapp: exports_external.object({
390029
- syncFullHistory: exports_external.boolean().optional().describe("Sync full message history on connect (default: true)")
390048
+ syncFullHistory: exports_external.boolean().optional().describe("Sync full message history on this connect (default: the persisted instance setting)")
390030
390049
  }).optional().describe("WhatsApp-specific connection options")
390031
390050
  });
390032
390051
  instancesRoutes.post("/:id/connect", instanceAccess3, zValidator("json", connectInstanceSchema.optional()), async (c) => {
@@ -390137,12 +390156,7 @@ var init_instances3 = __esm(() => {
390137
390156
  if (instance4.channel === "harness") {
390138
390157
  applyHarnessConnectionOptions(restartOptions, instance4.profileMetadata);
390139
390158
  }
390140
- if (instance4.channel === "whatsapp-baileys" && instance4.markOnlineOnConnect != null) {
390141
- restartOptions.whatsapp = {
390142
- ...restartOptions.whatsapp,
390143
- markOnlineOnConnect: instance4.markOnlineOnConnect
390144
- };
390145
- }
390159
+ applyWhatsAppBaileysOptions(restartOptions, instance4);
390146
390160
  await plugin13.connect(id, { instanceId: id, credentials: {}, options: restartOptions });
390147
390161
  } catch (error3) {
390148
390162
  return c.json({
@@ -530671,10 +530685,10 @@ var DEFAULT_SOCKET_CONFIG = {
530671
530685
  markOnlineOnConnect: true
530672
530686
  };
530673
530687
  function resolveHistoryIdentity(config2) {
530674
- const fullHistory = config2.syncFullHistory ?? DEFAULT_SOCKET_CONFIG.syncFullHistory;
530688
+ const desktop = (config2.historyIdentity ?? "desktop") === "desktop";
530675
530689
  return {
530676
- browser: config2.browser ?? (fullHistory ? Browsers.macOS("Desktop") : Browsers.ubuntu("Chrome")),
530677
- supportGroupHistory: config2.supportGroupHistory ?? fullHistory
530690
+ browser: config2.browser ?? (desktop ? Browsers.macOS("Desktop") : Browsers.ubuntu("Chrome")),
530691
+ supportGroupHistory: config2.supportGroupHistory ?? desktop
530678
530692
  };
530679
530693
  }
530680
530694
  var SUPPRESSED_LOG_PATTERNS = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260916.4",
3
+ "version": "2.260916.6",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/automagik-dev/omni.git",