@oxygen-agent/cli 1.377.3 → 1.591.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/column-run-notices.d.ts +11 -0
- package/dist/column-run-notices.js +37 -0
- package/dist/command-manifest.js +13 -8
- package/dist/help.js +79 -16
- package/dist/index.js +3812 -460
- package/dist/skills.js +106 -1
- package/node_modules/@oxygen/formula/dist/coerce.d.ts +8 -0
- package/node_modules/@oxygen/formula/dist/coerce.js +10 -0
- package/node_modules/@oxygen/formula/dist/evaluate.d.ts +31 -0
- package/node_modules/@oxygen/formula/dist/evaluate.js +248 -0
- package/node_modules/@oxygen/formula/dist/expression.d.ts +64 -0
- package/node_modules/@oxygen/formula/dist/expression.js +428 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.d.ts +71 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.js +1100 -0
- package/node_modules/@oxygen/formula/dist/index.d.ts +17 -0
- package/node_modules/@oxygen/formula/dist/index.js +17 -0
- package/node_modules/@oxygen/formula/dist/value-normalizers.d.ts +30 -0
- package/node_modules/@oxygen/formula/dist/value-normalizers.js +80 -0
- package/node_modules/@oxygen/formula/package.json +26 -0
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +30 -0
- package/node_modules/@oxygen/recipe-sdk/dist/index.js +2 -2
- package/node_modules/@oxygen/shared/dist/billing-anchors.d.ts +60 -0
- package/node_modules/@oxygen/shared/dist/billing-anchors.js +135 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +99 -5
- package/node_modules/@oxygen/shared/dist/billing.js +185 -8
- package/node_modules/@oxygen/shared/dist/call-outcomes.d.ts +59 -0
- package/node_modules/@oxygen/shared/dist/call-outcomes.js +73 -0
- package/node_modules/@oxygen/shared/dist/cli-result.js +1 -0
- package/node_modules/@oxygen/shared/dist/credit-guidance.js +3 -1
- package/node_modules/@oxygen/shared/dist/crm-reply-events.d.ts +35 -0
- package/node_modules/@oxygen/shared/dist/crm-reply-events.js +31 -0
- package/node_modules/@oxygen/shared/dist/dial-guardrail-overrides.d.ts +50 -0
- package/node_modules/@oxygen/shared/dist/dial-guardrail-overrides.js +65 -0
- package/node_modules/@oxygen/shared/dist/directory.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/directory.js +1 -0
- package/node_modules/@oxygen/shared/dist/file-import.js +58 -11
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +19 -0
- package/node_modules/@oxygen/shared/dist/index.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/index.js +9 -0
- package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.js +56 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +5 -4
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +5 -4
- package/node_modules/@oxygen/shared/dist/linkedin-url.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/linkedin-url.js +7 -4
- package/node_modules/@oxygen/shared/dist/log.js +41 -2
- package/node_modules/@oxygen/shared/dist/microsoft-consent-url.d.ts +7 -0
- package/node_modules/@oxygen/shared/dist/microsoft-consent-url.js +29 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +61 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +636 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.js +199 -0
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +89 -23
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +88 -24
- package/node_modules/@oxygen/shared/dist/sequence-crm-events.d.ts +291 -0
- package/node_modules/@oxygen/shared/dist/sequence-crm-events.js +224 -0
- package/node_modules/@oxygen/shared/dist/sequence-template.d.ts +42 -1
- package/node_modules/@oxygen/shared/dist/sequence-template.js +0 -0
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +287 -24
- package/node_modules/@oxygen/shared/dist/sequences.js +940 -60
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +70 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.js +106 -0
- package/node_modules/@oxygen/shared/dist/tags.d.ts +90 -1
- package/node_modules/@oxygen/shared/dist/tags.js +122 -6
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.js +4 -0
- package/node_modules/@oxygen/shared/package.json +95 -0
- package/node_modules/@oxygen/workflows/dist/event-dispatch.d.ts +126 -0
- package/node_modules/@oxygen/workflows/dist/event-dispatch.js +173 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.d.ts +78 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +700 -0
- package/node_modules/@oxygen/workflows/dist/graph/index.d.ts +20 -0
- package/node_modules/@oxygen/workflows/dist/graph/index.js +20 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.d.ts +4 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +812 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +501 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +200 -0
- package/node_modules/@oxygen/workflows/dist/graph/params.d.ts +86 -0
- package/node_modules/@oxygen/workflows/dist/graph/params.js +173 -0
- package/node_modules/@oxygen/workflows/dist/graph/remap.d.ts +48 -0
- package/node_modules/@oxygen/workflows/dist/graph/remap.js +213 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.d.ts +46 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.js +280 -0
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +270 -0
- package/node_modules/@oxygen/workflows/dist/graph/types.js +93 -0
- package/node_modules/@oxygen/workflows/dist/index.d.ts +113 -1
- package/node_modules/@oxygen/workflows/dist/index.js +179 -13
- package/node_modules/@oxygen/workflows/dist/tool-effects.d.ts +1 -0
- package/node_modules/@oxygen/workflows/dist/tool-effects.js +19 -0
- package/node_modules/@oxygen/workflows/dist/usage-estimate.js +135 -4
- package/node_modules/@oxygen/workflows/package.json +4 -0
- package/package.json +7 -5
|
@@ -27,7 +27,7 @@ import { type RenderTemplateOptions } from "./sequence-template.js";
|
|
|
27
27
|
* and inbox rotation while Oxygen owns the cross-channel timeline + reply
|
|
28
28
|
* suppression.
|
|
29
29
|
*/
|
|
30
|
-
export declare const SEQUENCE_CHANNELS: readonly ["linkedin", "email", "whatsapp"];
|
|
30
|
+
export declare const SEQUENCE_CHANNELS: readonly ["linkedin", "email", "whatsapp", "call", "crm"];
|
|
31
31
|
export type SequenceChannel = (typeof SEQUENCE_CHANNELS)[number];
|
|
32
32
|
/**
|
|
33
33
|
* Signals an enrollment accumulates from provider webhooks. wait_for_signal gates
|
|
@@ -36,7 +36,7 @@ export type SequenceChannel = (typeof SEQUENCE_CHANNELS)[number];
|
|
|
36
36
|
* a signal — it's resolved on demand by the dispatcher via a connection branch,
|
|
37
37
|
* `condition: "already_connected"`.)
|
|
38
38
|
*/
|
|
39
|
-
export declare const SEQUENCE_SIGNALS: readonly ["linkedin_connected", "linkedin_replied", "whatsapp_replied", "email_sent", "email_opened", "email_clicked", "email_replied", "email_bounced", "meeting_booked", "email_unsubscribed", "company_hiring", "company_raised_funds", "job_change", "new_hire", "web_visit", "intent"];
|
|
39
|
+
export declare const SEQUENCE_SIGNALS: readonly ["linkedin_connected", "linkedin_replied", "whatsapp_replied", "email_sent", "email_opened", "email_clicked", "email_replied", "email_bounced", "meeting_booked", "email_unsubscribed", "call_connected", "call_no_answer", "call_voicemail", "company_hiring", "company_raised_funds", "job_change", "new_hire", "web_visit", "intent"];
|
|
40
40
|
export type SequenceSignal = (typeof SEQUENCE_SIGNALS)[number];
|
|
41
41
|
/**
|
|
42
42
|
* Email engagement signals that ONLY arrive via the Instantly webhook
|
|
@@ -49,6 +49,27 @@ export type SequenceSignal = (typeof SEQUENCE_SIGNALS)[number];
|
|
|
49
49
|
*/
|
|
50
50
|
export declare const SEQUENCE_NATIVE_UNTRACKED_SIGNALS: readonly ["email_opened", "email_clicked"];
|
|
51
51
|
export type SequenceNativeUntrackedSignal = (typeof SEQUENCE_NATIVE_UNTRACKED_SIGNALS)[number];
|
|
52
|
+
/**
|
|
53
|
+
* Signals that ALWAYS terminalize the enrollment the moment they fire, so no
|
|
54
|
+
* later step can ever observe them. A reply on any channel unconditionally stops
|
|
55
|
+
* the replying lead's enrollment (`status: 'replied'`, reason `lead_replied` —
|
|
56
|
+
* see the LinkedIn / WhatsApp / email inbound paths), and the one-click
|
|
57
|
+
* List-Unsubscribe webhook stops it too (`unsubscribed_one_click`).
|
|
58
|
+
*
|
|
59
|
+
* They are still RECORDED on the enrollment (analytics, the timeline, and the
|
|
60
|
+
* reply-rate rollups all read them), but a branch or wait_for_signal gated on one
|
|
61
|
+
* is dead code: the enrollment is already terminal when the signal lands, so the
|
|
62
|
+
* gate always times out / always takes the `else` arm. The builder therefore does
|
|
63
|
+
* not offer them as branch or gate conditions — reply-stop is built in.
|
|
64
|
+
*/
|
|
65
|
+
export declare const SEQUENCE_TERMINAL_SIGNALS: readonly ["linkedin_replied", "whatsapp_replied", "email_replied", "email_unsubscribed"];
|
|
66
|
+
export type SequenceTerminalSignal = (typeof SEQUENCE_TERMINAL_SIGNALS)[number];
|
|
67
|
+
export declare function isTerminalSequenceSignal(value: string): value is SequenceTerminalSignal;
|
|
68
|
+
/**
|
|
69
|
+
* The signals a branch / wait_for_signal condition can meaningfully read — every
|
|
70
|
+
* signal except the terminal ones above. This is what authoring surfaces offer.
|
|
71
|
+
*/
|
|
72
|
+
export declare const SEQUENCE_BRANCHABLE_SIGNALS: readonly SequenceSignal[];
|
|
52
73
|
/** External GTM signals (the non-engagement subset of SEQUENCE_SIGNALS). */
|
|
53
74
|
export declare const SEQUENCE_EXTERNAL_SIGNALS: readonly ["company_hiring", "company_raised_funds", "job_change", "new_hire", "web_visit", "intent"];
|
|
54
75
|
export type SequenceExternalSignal = (typeof SEQUENCE_EXTERNAL_SIGNALS)[number];
|
|
@@ -67,17 +88,20 @@ export declare const SEQUENCE_EMAIL_COLUMN_KEYS: readonly ["email", "email_addre
|
|
|
67
88
|
* complaint loop (the address a bounce/opt-out suppresses), so they can't drift.
|
|
68
89
|
* Returns the trimmed raw value (callers normalize/lowercase as needed) or null.
|
|
69
90
|
*/
|
|
70
|
-
export declare function recipientEmailFromRow(rowValues: Record<string, unknown> | null | undefined): string | null;
|
|
91
|
+
export declare function recipientEmailFromRow(rowValues: Record<string, unknown> | null | undefined, emailColumnKey?: string | null): string | null;
|
|
71
92
|
/**
|
|
72
93
|
* ESP (email service provider) MATCHING mode for a sequence (settings.esp_matching).
|
|
73
94
|
* Deliverability lore: a send lands better when the SENDING mailbox and the
|
|
74
95
|
* RECIPIENT sit on the same provider (Google→Google, Microsoft→Microsoft), so at
|
|
75
96
|
* mailbox-pick the dispatcher can bias rotation toward a same-ESP mailbox.
|
|
76
|
-
* - "off" — no ESP bias
|
|
77
|
-
* - "prefer" — pick a same-ESP mailbox when one is available, else
|
|
78
|
-
* any sendable mailbox (never blocks a send).
|
|
97
|
+
* - "off" — no ESP bias; rotation is pure LRU / domain-spread.
|
|
98
|
+
* - "prefer" — (DEFAULT) pick a same-ESP mailbox when one is available, else
|
|
99
|
+
* fall back to any sendable mailbox (never blocks a send). On by default
|
|
100
|
+
* because it only ever improves deliverability and cannot starve sends.
|
|
79
101
|
* - "strict" — require a same-ESP mailbox; when none is available the action is
|
|
80
102
|
* DEFERRED with a visible skipped_reason rather than sent cross-provider.
|
|
103
|
+
* Power-user setting, reachable via CLI/MCP; the web "Provider Matching"
|
|
104
|
+
* checkbox toggles off↔prefer only.
|
|
81
105
|
*/
|
|
82
106
|
export declare const ESP_MATCHING_MODES: readonly ["off", "prefer", "strict"];
|
|
83
107
|
export type EspMatchingMode = (typeof ESP_MATCHING_MODES)[number];
|
|
@@ -92,28 +116,78 @@ export declare function isEspMatchingMode(value: unknown): value is EspMatchingM
|
|
|
92
116
|
*/
|
|
93
117
|
export declare function validateEspMatchingSetting(value: unknown): void;
|
|
94
118
|
/**
|
|
95
|
-
* The email-provider
|
|
96
|
-
* intentionally aligned with EMAIL_MAILBOX_PROVIDERS in
|
|
97
|
-
* `provider` and
|
|
119
|
+
* The MATCHABLE email-provider a recipient routes through — the narrow set ESP
|
|
120
|
+
* matching can act on. Kept intentionally aligned with EMAIL_MAILBOX_PROVIDERS in
|
|
121
|
+
* tenant-db so a mailbox's `provider` and a resolved recipient ESP compare
|
|
122
|
+
* directly: we can only bias rotation toward a provider we can actually SEND
|
|
123
|
+
* from, and the pool is Google/Microsoft only.
|
|
124
|
+
*
|
|
125
|
+
* This is deliberately NARROWER than RECIPIENT_ESP_FAMILIES (what we DISPLAY).
|
|
126
|
+
* A domain behind Proofpoint has family "proofpoint" but may still be matchable
|
|
127
|
+
* as "microsoft" — see resolveEspFamily.
|
|
98
128
|
*/
|
|
99
129
|
export declare const RECIPIENT_ESPS: readonly ["google", "microsoft"];
|
|
100
130
|
export type RecipientEsp = (typeof RECIPIENT_ESPS)[number];
|
|
101
131
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
132
|
+
* The DISPLAY taxonomy: the mail-infrastructure family a recipient domain sits
|
|
133
|
+
* on. Wider than RecipientEsp because "everything that isn't Google or Microsoft"
|
|
134
|
+
* is not one thing — a list that is 23% Proofpoint is a very different
|
|
135
|
+
* deliverability problem from one that is 23% self-hosted, and collapsing both to
|
|
136
|
+
* null (today's behavior) hides that. Split into three groups:
|
|
137
|
+
*
|
|
138
|
+
* - MAILBOX providers (google, microsoft) — imply a matchable ESP.
|
|
139
|
+
* - GATEWAY vendors (proofpoint, mimecast, ...) — security filters that sit IN
|
|
140
|
+
* FRONT of a mailbox provider. Their MX tells you the filter, NOT where the
|
|
141
|
+
* mailbox lives; the SPF fallback is what sees behind them.
|
|
142
|
+
* - OTHER mailbox hosts (zoho, yahoo, fastmail, ...) and self_hosted — real
|
|
143
|
+
* destinations we simply cannot send as, so never matchable.
|
|
144
|
+
*/
|
|
145
|
+
export declare const RECIPIENT_ESP_FAMILIES: readonly ["google", "microsoft", "proofpoint", "mimecast", "barracuda", "cisco", "zscaler", "trendmicro", "sophos", "zoho", "yahoo", "apple", "proton", "fastmail", "gmx", "amazon", "self_hosted"];
|
|
146
|
+
export type RecipientEspFamily = (typeof RECIPIENT_ESP_FAMILIES)[number];
|
|
147
|
+
export declare function isRecipientEspFamily(value: unknown): value is RecipientEspFamily;
|
|
148
|
+
export declare function isEspGatewayFamily(family: RecipientEspFamily | null | undefined): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* The matchable ESP a display family implies, or null when the family is a
|
|
151
|
+
* gateway (unknown until SPF) or a provider we cannot send as. The ONLY place
|
|
152
|
+
* family → esp is decided, so display and matching can't drift.
|
|
153
|
+
*/
|
|
154
|
+
export declare function matchableEspFromFamily(family: RecipientEspFamily | null | undefined): RecipientEsp | null;
|
|
155
|
+
/**
|
|
156
|
+
* Map a domain's resolved MX exchange hostnames to a DISPLAY family. Returns null
|
|
157
|
+
* when no host matches any known vendor — the caller records that as
|
|
158
|
+
* "self_hosted" only once it is sure the lookup itself succeeded (an empty/failed
|
|
159
|
+
* answer is "unknown", which is a different fact).
|
|
160
|
+
*/
|
|
161
|
+
export declare function familyFromMxHosts(hosts: readonly string[] | null | undefined): RecipientEspFamily | null;
|
|
162
|
+
/**
|
|
163
|
+
* Resolve the matchable ESP from a domain's TXT records by reading its SPF
|
|
164
|
+
* mechanisms. Scans every `v=spf1` record's include:/redirect= targets.
|
|
165
|
+
*
|
|
166
|
+
* AMBIGUITY IS NOT GUESSED: a domain mid-migration (or running Workspace
|
|
167
|
+
* alongside an Office tenant) can include BOTH vendors. Returning either one
|
|
168
|
+
* would be a coin flip that strict matching then acts on, so a both-match
|
|
169
|
+
* degrades to null ("unknown") and the caller falls back / defers honestly.
|
|
170
|
+
*/
|
|
171
|
+
export declare function espFromSpfRecords(records: readonly string[] | null | undefined): RecipientEsp | null;
|
|
172
|
+
/**
|
|
173
|
+
* Infer a recipient's DISPLAY family from its domain string alone,
|
|
174
|
+
* DETERMINISTICALLY, for the well-known consumer/freemail domains. Returns null
|
|
175
|
+
* for any other domain — the caller then resolves via the cached MX (+ SPF)
|
|
176
|
+
* lookup, because a custom domain fronted by Workspace / M365 / a gateway is not
|
|
177
|
+
* decidable from the string. Pure: the single source of truth for the map.
|
|
178
|
+
*/
|
|
179
|
+
export declare function inferRecipientEspFamily(domain: string | null | undefined): RecipientEspFamily | null;
|
|
180
|
+
/**
|
|
181
|
+
* Infer a recipient's MATCHABLE ESP from its domain string. Narrow by design:
|
|
182
|
+
* only the Google/Microsoft consumer domains resolve here, everything else
|
|
183
|
+
* (including yahoo/proton/icloud, which are real but unsendable-as) is null.
|
|
184
|
+
* Unchanged contract — the dispatcher's fast path still calls this.
|
|
108
185
|
*/
|
|
109
186
|
export declare function inferRecipientEsp(domain: string | null | undefined): RecipientEsp | null;
|
|
110
187
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* hosts end in `outlook.com` / `protection.outlook.com` (e.g.
|
|
115
|
-
* acme-com.mail.protection.outlook.com). Returns null when no host matches either
|
|
116
|
-
* family. Pure so both the resolver and its tests share one mapping.
|
|
188
|
+
* Back-compat narrow view of familyFromMxHosts: MX hosts → matchable ESP. Now
|
|
189
|
+
* derived from the family table so the two can't drift, and gateway MX hosts
|
|
190
|
+
* correctly yield null here (they need the SPF signal to become matchable).
|
|
117
191
|
*/
|
|
118
192
|
export declare function espFromMxHosts(hosts: readonly string[] | null | undefined): RecipientEsp | null;
|
|
119
193
|
/**
|
|
@@ -142,6 +216,35 @@ export declare function whatsAppAttendeeIdForPhone(phone: string): string | null
|
|
|
142
216
|
* enroll/plan path and any preview count can't drift.
|
|
143
217
|
*/
|
|
144
218
|
export declare function whatsAppAttendeeIdFromRow(rowValues: Record<string, unknown> | null | undefined, phoneColumnKey?: string | null): string | null;
|
|
219
|
+
/**
|
|
220
|
+
* Normalize a raw phone number to strict E.164 (`+` then 2-15 digits, no leading
|
|
221
|
+
* zero) for the CALL channel. Deliberately stricter than
|
|
222
|
+
* whatsAppAttendeeIdForPhone, which accepts any 7-15 bare digits: on WhatsApp a
|
|
223
|
+
* bad guess fails an API call, but on the phone it rings a real stranger.
|
|
224
|
+
*
|
|
225
|
+
* Rules — anything ambiguous returns null rather than guessing:
|
|
226
|
+
* - already "+…" -> strip separators, validate, keep.
|
|
227
|
+
* - 10 bare digits -> NANP national number, prefix "+1".
|
|
228
|
+
* - 11 bare digits starting with "1" -> NANP with country code, prefix "+".
|
|
229
|
+
* - anything else -> null.
|
|
230
|
+
*
|
|
231
|
+
* SCOPE: bare (non-"+") input is completed ONLY for the NANP, where the national
|
|
232
|
+
* number is always exactly 10 digits. Every other country has a variable national
|
|
233
|
+
* length (UK 10, Germany 6-11, …), so a bare string genuinely cannot be resolved
|
|
234
|
+
* to one number — those callers must supply full E.164. That matches v1, which
|
|
235
|
+
* provisions US/Canada numbers only.
|
|
236
|
+
*
|
|
237
|
+
* When international dialing lands, replace this with libphonenumber-js rather
|
|
238
|
+
* than extending the heuristic — per-country national-number rules are a lookup
|
|
239
|
+
* table, not something to hand-roll against live prospects.
|
|
240
|
+
*/
|
|
241
|
+
export declare function callPhoneForLead(phone: string): string | null;
|
|
242
|
+
/**
|
|
243
|
+
* Resolve a lead's dialable E.164 number from its row_values, reusing the SAME
|
|
244
|
+
* column precedence WhatsApp uses (SEQUENCE_PHONE_COLUMN_KEYS) so the two phone
|
|
245
|
+
* channels can never disagree about which column holds the number.
|
|
246
|
+
*/
|
|
247
|
+
export declare function callPhoneFromRow(rowValues: Record<string, unknown> | null | undefined, phoneColumnKey?: string | null): string | null;
|
|
145
248
|
/**
|
|
146
249
|
* A per-step / per-sequence send window. `days` are ISO weekdays (1=Mon … 7=Sun)
|
|
147
250
|
* on which sends are allowed; `start`/`end` are "HH:MM" local times. The window
|
|
@@ -215,7 +318,7 @@ export type SequenceAutoOptimizeConfig = {
|
|
|
215
318
|
min_sends_per_variant: number;
|
|
216
319
|
min_conversions: number;
|
|
217
320
|
};
|
|
218
|
-
export declare const SEQUENCE_STEP_KINDS: readonly ["visit_profile", "invite", "wait_for_connection", "message", "inmail", "follow", "like_post", "comment_post", "withdraw_invite", "email_send", "email_reply", "email_enroll", "email_move", "email_stop", "whatsapp_message", "wait", "wait_for_signal", "branch", "stop"];
|
|
321
|
+
export declare const SEQUENCE_STEP_KINDS: readonly ["visit_profile", "invite", "wait_for_connection", "message", "inmail", "follow", "like_post", "comment_post", "withdraw_invite", "email_send", "email_reply", "email_enroll", "email_move", "email_stop", "whatsapp_message", "call_task", "crm_task", "wait", "wait_for_signal", "branch", "stop"];
|
|
219
322
|
export type SequenceStepKind = (typeof SEQUENCE_STEP_KINDS)[number];
|
|
220
323
|
export type SequenceLinkedInVisitProfileStep = {
|
|
221
324
|
id: string;
|
|
@@ -384,6 +487,137 @@ export type SequenceWhatsAppMessageStep = {
|
|
|
384
487
|
/** Opt-in reversible auto-winner over the variants (metric: reply | click | open; reply recommended). */
|
|
385
488
|
auto_optimize?: SequenceAutoOptimizeConfig;
|
|
386
489
|
};
|
|
490
|
+
/**
|
|
491
|
+
* Queue a call for a human. The one step kind Oxygen does not execute itself.
|
|
492
|
+
*
|
|
493
|
+
* Planning it inserts an ox_sequencer.call_tasks row and parks the enrollment
|
|
494
|
+
* awaiting a call signal, exactly like wait_for_signal — dispositioning the call
|
|
495
|
+
* writes call_connected / call_no_answer / call_voicemail, which resumes it. That
|
|
496
|
+
* is why `timeout_days` and `on_timeout` mirror the wait_for_signal shape: a lead
|
|
497
|
+
* nobody ever calls must not sit in a sequence forever.
|
|
498
|
+
*
|
|
499
|
+
* `note` is talking points shown to the rep on the dial screen, NOT a message —
|
|
500
|
+
* it is never sent anywhere, so it is optional and a blank one does not block a
|
|
501
|
+
* launch the way empty message copy does.
|
|
502
|
+
*/
|
|
503
|
+
export type SequenceCallTaskStep = {
|
|
504
|
+
id: string;
|
|
505
|
+
channel: "call";
|
|
506
|
+
kind: "call_task";
|
|
507
|
+
/** Talking points for the rep. Supports {{column}}. Never transmitted. */
|
|
508
|
+
note?: string;
|
|
509
|
+
/** Queue ordering; higher dials first. Defaults to 0. */
|
|
510
|
+
priority?: number;
|
|
511
|
+
/** Days to wait for a disposition before acting on on_timeout. */
|
|
512
|
+
timeout_days: number;
|
|
513
|
+
/** "stop" ends the sequence if nobody calls; "continue" advances. Defaults to "continue". */
|
|
514
|
+
on_timeout: "stop" | "continue";
|
|
515
|
+
};
|
|
516
|
+
export declare const SEQUENCE_CRM_TASK_PROVIDERS: readonly ["hubspot"];
|
|
517
|
+
export type SequenceCrmTaskProvider = (typeof SEQUENCE_CRM_TASK_PROVIDERS)[number];
|
|
518
|
+
/**
|
|
519
|
+
* Provider-property value mapping for a CRM task. Keeping the source shape
|
|
520
|
+
* provider-neutral means a future Salesforce/Pipedrive adapter can consume the
|
|
521
|
+
* exact same sequence definition while mapping different destination property
|
|
522
|
+
* names.
|
|
523
|
+
*/
|
|
524
|
+
export type SequenceCrmTaskValueMapping = {
|
|
525
|
+
source: "literal";
|
|
526
|
+
value: string | number | boolean;
|
|
527
|
+
} | {
|
|
528
|
+
source: "row";
|
|
529
|
+
key: string;
|
|
530
|
+
} | {
|
|
531
|
+
source: "template";
|
|
532
|
+
template: string;
|
|
533
|
+
} | {
|
|
534
|
+
source: "relative_time";
|
|
535
|
+
offset_minutes: number;
|
|
536
|
+
};
|
|
537
|
+
export declare const SEQUENCE_CRM_IDENTITY_KINDS: readonly ["provider_id", "email", "domain", "linkedin_url", "exact"];
|
|
538
|
+
export type SequenceCrmIdentityKind = (typeof SEQUENCE_CRM_IDENTITY_KINDS)[number];
|
|
539
|
+
/**
|
|
540
|
+
* One exact identity used to resolve an existing provider record. `kind`
|
|
541
|
+
* controls normalization while `property` remains provider-configurable (for
|
|
542
|
+
* example HubSpot's `email` or a customer's custom LinkedIn URL property).
|
|
543
|
+
*/
|
|
544
|
+
export type SequenceCrmRecordIdentityMapping = {
|
|
545
|
+
kind: SequenceCrmIdentityKind;
|
|
546
|
+
/** Destination property internal name. Omitted only for provider_id. */
|
|
547
|
+
property?: string;
|
|
548
|
+
value: SequenceCrmTaskValueMapping;
|
|
549
|
+
};
|
|
550
|
+
/**
|
|
551
|
+
* A named person/account/deal binding resolved before the task is created.
|
|
552
|
+
* Names are local sequence references; object/property names are deliberately
|
|
553
|
+
* dynamic so another CRM adapter can consume the same lifecycle.
|
|
554
|
+
*/
|
|
555
|
+
export type SequenceCrmRecordMapping = {
|
|
556
|
+
/** Destination object type, e.g. contacts, companies, people, or deals. */
|
|
557
|
+
object_type: string;
|
|
558
|
+
/** Ordered exact identities. Name-only matching is intentionally unsupported. */
|
|
559
|
+
identities: SequenceCrmRecordIdentityMapping[];
|
|
560
|
+
/** Dynamic provider properties applied on create/update. */
|
|
561
|
+
property_mappings: Record<string, SequenceCrmTaskValueMapping>;
|
|
562
|
+
/** Missing records are only created when explicitly authorized. */
|
|
563
|
+
on_missing: "fail" | "create";
|
|
564
|
+
/** Existing records are reused by default; update must be explicit. */
|
|
565
|
+
on_match: "reuse" | "update";
|
|
566
|
+
};
|
|
567
|
+
export type SequenceCrmRecordLink = {
|
|
568
|
+
from_record: string;
|
|
569
|
+
to_record: string;
|
|
570
|
+
/**
|
|
571
|
+
* Optional provider association type. HubSpot's default relation is used when
|
|
572
|
+
* absent; custom labels/types can be supplied without changing the DSL.
|
|
573
|
+
*/
|
|
574
|
+
association_type_id?: number;
|
|
575
|
+
association_category?: "HUBSPOT_DEFINED" | "USER_DEFINED";
|
|
576
|
+
};
|
|
577
|
+
type SequenceCrmTaskAssociationOptions = {
|
|
578
|
+
association_type_id?: number;
|
|
579
|
+
association_category?: "HUBSPOT_DEFINED" | "USER_DEFINED";
|
|
580
|
+
};
|
|
581
|
+
export type SequenceCrmTaskAssociation = SequenceCrmTaskAssociationOptions & ({
|
|
582
|
+
/** Attach the task to a record resolved by record_mappings. */
|
|
583
|
+
record_ref: string;
|
|
584
|
+
/** Optional override; otherwise the referenced record's object_type wins. */
|
|
585
|
+
object_type?: string;
|
|
586
|
+
object_id?: never;
|
|
587
|
+
} | {
|
|
588
|
+
/** Destination CRM object type, e.g. contacts, companies, or deals. */
|
|
589
|
+
object_type: string;
|
|
590
|
+
/** Provider record id resolved from a literal, source-row field, or template. */
|
|
591
|
+
object_id: SequenceCrmTaskValueMapping;
|
|
592
|
+
record_ref?: never;
|
|
593
|
+
});
|
|
594
|
+
/**
|
|
595
|
+
* Create one task in a connected CRM and advance after the provider confirms
|
|
596
|
+
* creation. Unlike call_task this is an external write, not a human wait gate.
|
|
597
|
+
*/
|
|
598
|
+
export type SequenceCrmTaskStep = {
|
|
599
|
+
id: string;
|
|
600
|
+
channel: "crm";
|
|
601
|
+
kind: "crm_task";
|
|
602
|
+
provider: SequenceCrmTaskProvider;
|
|
603
|
+
/** Optional explicit connection; absent resolves the workspace's active one. */
|
|
604
|
+
connection_id?: string;
|
|
605
|
+
/**
|
|
606
|
+
* Destination property internal-name → value source. Property names remain
|
|
607
|
+
* dynamic so custom CRM fields do not require a new Oxygen release.
|
|
608
|
+
*/
|
|
609
|
+
property_mappings: Record<string, SequenceCrmTaskValueMapping>;
|
|
610
|
+
/**
|
|
611
|
+
* Optional named provider records to resolve/upsert before creating the task.
|
|
612
|
+
* Exact identity matches are reused, conflicting identities fail closed, and
|
|
613
|
+
* provider writes are checkpointed independently by the runtime.
|
|
614
|
+
*/
|
|
615
|
+
record_mappings?: Record<string, SequenceCrmRecordMapping>;
|
|
616
|
+
/** Optional links between named records, e.g. person → account. */
|
|
617
|
+
record_links?: SequenceCrmRecordLink[];
|
|
618
|
+
/** Optional records to associate the newly-created task with. */
|
|
619
|
+
associations?: SequenceCrmTaskAssociation[];
|
|
620
|
+
};
|
|
387
621
|
export type SequenceWaitStep = {
|
|
388
622
|
id: string;
|
|
389
623
|
kind: "wait";
|
|
@@ -501,7 +735,7 @@ export type SequenceSignalCondition = {
|
|
|
501
735
|
} | {
|
|
502
736
|
not: SequenceSignalCondition;
|
|
503
737
|
};
|
|
504
|
-
export type SequenceStep = SequenceLinkedInVisitProfileStep | SequenceLinkedInInviteStep | SequenceLinkedInWaitForConnectionStep | SequenceLinkedInMessageStep | SequenceLinkedInInMailStep | SequenceLinkedInFollowStep | SequenceLinkedInLikePostStep | SequenceLinkedInCommentPostStep | SequenceLinkedInWithdrawInviteStep | SequenceEmailSendStep | SequenceEmailReplyStep | SequenceEmailEnrollStep | SequenceEmailMoveStep | SequenceEmailStopStep | SequenceWhatsAppMessageStep | SequenceWaitStep | SequenceWaitForSignalStep | SequenceBranchStep | SequenceStopStep;
|
|
738
|
+
export type SequenceStep = SequenceLinkedInVisitProfileStep | SequenceLinkedInInviteStep | SequenceLinkedInWaitForConnectionStep | SequenceLinkedInMessageStep | SequenceLinkedInInMailStep | SequenceLinkedInFollowStep | SequenceLinkedInLikePostStep | SequenceLinkedInCommentPostStep | SequenceLinkedInWithdrawInviteStep | SequenceEmailSendStep | SequenceEmailReplyStep | SequenceEmailEnrollStep | SequenceEmailMoveStep | SequenceEmailStopStep | SequenceWhatsAppMessageStep | SequenceCallTaskStep | SequenceCrmTaskStep | SequenceWaitStep | SequenceWaitForSignalStep | SequenceBranchStep | SequenceStopStep;
|
|
505
739
|
export type SequenceDefinition = {
|
|
506
740
|
steps: SequenceStep[];
|
|
507
741
|
};
|
|
@@ -526,7 +760,35 @@ export type ValidateSequenceOptions = {
|
|
|
526
760
|
* the gate would silently dead-end).
|
|
527
761
|
*/
|
|
528
762
|
nativeTrackingEnabled?: boolean;
|
|
763
|
+
/**
|
|
764
|
+
* This definition is a DRAFT being authored, so permit an incomplete authoring
|
|
765
|
+
* state: a sequence with no steps yet, and steps whose copy has not been
|
|
766
|
+
* written. Default false — a launch-ready definition must have both.
|
|
767
|
+
*
|
|
768
|
+
* The web builder autosaves on every edit, so it necessarily persists states no
|
|
769
|
+
* launch-ready sequence may have: the moment you delete the last step, or drop a
|
|
770
|
+
* "Send message" before typing it. Rejecting those wedged autosave — the
|
|
771
|
+
* definition could not be stored at all, so the edit was lost on navigate.
|
|
772
|
+
*
|
|
773
|
+
* Completeness is a LAUNCH concern, not a storage one. `sequenceStepsMissingCopy`
|
|
774
|
+
* and an empty-steps check are the gates the start route runs before a sequence
|
|
775
|
+
* may go active, and draft -> active is the only route to sending (the status
|
|
776
|
+
* route refuses that transition outright).
|
|
777
|
+
*/
|
|
778
|
+
draft?: boolean;
|
|
529
779
|
};
|
|
780
|
+
/** One step whose copy is still empty, for the launch-readiness blocker. */
|
|
781
|
+
export type SequenceMissingCopy = {
|
|
782
|
+
stepId: string;
|
|
783
|
+
kind: SequenceStepKind;
|
|
784
|
+
field: string;
|
|
785
|
+
};
|
|
786
|
+
/**
|
|
787
|
+
* Steps that still have empty copy — the launch gate for definitions stored
|
|
788
|
+
* stored under `draft`. Empty means nothing to send, so starting would
|
|
789
|
+
* either dispatch a blank message or fail per lead at send time.
|
|
790
|
+
*/
|
|
791
|
+
export declare function sequenceStepsMissingCopy(steps: readonly SequenceStep[]): SequenceMissingCopy[];
|
|
530
792
|
/**
|
|
531
793
|
* Validate + normalize a raw sequence definition. Assigns stable ids to steps
|
|
532
794
|
* that omit one (s{index}), verifies branch/gate targets resolve, and enforces
|
|
@@ -556,7 +818,8 @@ export declare function sequenceWaitStepDelayWithJitterMs(step: SequenceWaitStep
|
|
|
556
818
|
/**
|
|
557
819
|
* Render a sequence-copy template against a row's values. Delegates to the shared
|
|
558
820
|
* deterministic engine (sequence-template.ts): `{{column}}` substitution plus
|
|
559
|
-
* `{{column|fallback}}`, `{{RANDOM|…}}`
|
|
821
|
+
* `{{column|fallback}}`, `{{RANDOM|…}}` and bare `{a|b|c}` spintax (nestable), and
|
|
822
|
+
* `{% if … %}` conditionals.
|
|
560
823
|
* Pass `{ seed }` to make spintax choices replayable across retries/crash-replays.
|
|
561
824
|
*/
|
|
562
825
|
export declare function renderSequenceTemplate(template: string, values: Record<string, unknown>, options?: RenderTemplateOptions): string;
|