@oxygen-agent/cli 1.987.20 → 1.1010.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/admin-primary-providers-render.d.ts +0 -2
- package/dist/admin-primary-providers-render.js +1 -1
- package/dist/browser-login.js +1 -4
- package/dist/column-decision-options.d.ts +20 -0
- package/dist/column-decision-options.js +54 -0
- package/dist/command-manifest.d.ts +3 -2
- package/dist/command-manifest.js +25 -2
- package/dist/credentials.d.ts +1 -1
- package/dist/functions-commands.js +13 -9
- package/dist/help.d.ts +8 -0
- package/dist/help.js +46 -0
- package/dist/index.js +2751 -242
- package/dist/knowledge-mirror.d.ts +2 -2
- package/dist/runtime.d.ts +0 -15
- package/dist/runtime.js +1 -1
- package/dist/search-ai-filter-notice.d.ts +17 -0
- package/dist/search-ai-filter-notice.js +38 -0
- package/dist/session.d.ts +4 -3
- package/dist/skills.d.ts +8 -7
- package/dist/skills.js +58 -20
- package/dist/transcript.d.ts +2 -1
- package/dist/util.d.ts +10 -1
- package/dist/util.js +14 -2
- package/node_modules/@oxygen/cli-ugc/dist/commands.js +296 -140
- package/node_modules/@oxygen/cli-ugc/dist/field-parser.d.ts +9 -0
- package/node_modules/@oxygen/cli-ugc/dist/field-parser.js +34 -0
- package/node_modules/@oxygen/formula/dist/coerce.d.ts +10 -0
- package/node_modules/@oxygen/formula/dist/coerce.js +10 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.js +65 -0
- package/node_modules/@oxygen/formula/dist/hash.d.ts +19 -0
- package/node_modules/@oxygen/formula/dist/hash.js +199 -0
- package/node_modules/@oxygen/formula/dist/value-cleaners.d.ts +6 -1
- package/node_modules/@oxygen/formula/dist/value-cleaners.js +10 -26
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +13 -0
- package/node_modules/@oxygen/shared/dist/array-utils.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/array-utils.js +11 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +99 -22
- package/node_modules/@oxygen/shared/dist/billing.js +195 -40
- package/node_modules/@oxygen/shared/dist/byok-connect.js +5 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +27 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +311 -28
- package/node_modules/@oxygen/shared/dist/cli-http-error.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/cli-http-error.js +8 -0
- package/node_modules/@oxygen/shared/dist/cli-result.js +1 -0
- package/node_modules/@oxygen/shared/dist/column-autofill.d.ts +52 -0
- package/node_modules/@oxygen/shared/dist/column-autofill.js +62 -0
- package/node_modules/@oxygen/shared/dist/column-decision.d.ts +50 -0
- package/node_modules/@oxygen/shared/dist/column-decision.js +228 -0
- package/node_modules/@oxygen/shared/dist/column-output-fields.js +2 -6
- package/node_modules/@oxygen/shared/dist/company-enrichment-fields.d.ts +113 -0
- package/node_modules/@oxygen/shared/dist/company-enrichment-fields.js +548 -0
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +6 -6
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +6 -6
- package/node_modules/@oxygen/shared/dist/cutover-freeze.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/cutover-freeze.js +52 -0
- package/node_modules/@oxygen/shared/dist/data-suppliers.d.ts +57 -0
- package/node_modules/@oxygen/shared/dist/data-suppliers.js +59 -0
- package/node_modules/@oxygen/shared/dist/deploy-env.d.ts +74 -0
- package/node_modules/@oxygen/shared/dist/deploy-env.js +82 -0
- package/node_modules/@oxygen/shared/dist/dnc-rules.d.ts +130 -0
- package/node_modules/@oxygen/shared/dist/dnc-rules.js +221 -0
- package/node_modules/@oxygen/shared/dist/enrichment-intents.d.ts +107 -0
- package/node_modules/@oxygen/shared/dist/enrichment-intents.js +809 -0
- package/node_modules/@oxygen/shared/dist/error-message.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/error-message.js +3 -0
- package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -3
- package/node_modules/@oxygen/shared/dist/external-write-policy.d.ts +33 -0
- package/node_modules/@oxygen/shared/dist/external-write-policy.js +68 -0
- package/node_modules/@oxygen/shared/dist/format-percent.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/format-percent.js +13 -0
- package/node_modules/@oxygen/shared/dist/freemail-domains.d.ts +81 -0
- package/node_modules/@oxygen/shared/dist/freemail-domains.js +157 -0
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +1 -1
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +60 -4
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +125 -10
- package/node_modules/@oxygen/shared/dist/index.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/index.js +15 -0
- package/node_modules/@oxygen/shared/dist/json-path.js +1 -3
- package/node_modules/@oxygen/shared/dist/knowledge-bases.js +1 -3
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +2 -2
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +2 -2
- package/node_modules/@oxygen/shared/dist/langfuse.d.ts +44 -1
- package/node_modules/@oxygen/shared/dist/langfuse.js +416 -14
- package/node_modules/@oxygen/shared/dist/linkedin-countries.d.ts +33 -0
- package/node_modules/@oxygen/shared/dist/linkedin-countries.js +361 -0
- package/node_modules/@oxygen/shared/dist/linkedin-country-timezones.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/linkedin-country-timezones.js +276 -0
- package/node_modules/@oxygen/shared/dist/linkedin-message-deletion.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/linkedin-message-deletion.js +5 -0
- package/node_modules/@oxygen/shared/dist/linkedin-post-keywords.d.ts +44 -0
- package/node_modules/@oxygen/shared/dist/linkedin-post-keywords.js +116 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +96 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +123 -0
- package/node_modules/@oxygen/shared/dist/llm-durable-capture.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/llm-durable-capture.js +89 -0
- package/node_modules/@oxygen/shared/dist/llm-prompts.d.ts +75 -0
- package/node_modules/@oxygen/shared/dist/llm-prompts.js +161 -0
- package/node_modules/@oxygen/shared/dist/log-sink-selector.d.ts +39 -0
- package/node_modules/@oxygen/shared/dist/log-sink-selector.js +56 -0
- package/node_modules/@oxygen/shared/dist/log.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/log.js +6 -1
- package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.d.ts +90 -0
- package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.js +130 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +21 -0
- package/node_modules/@oxygen/shared/dist/operational-telemetry.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/operational-telemetry.js +73 -0
- package/node_modules/@oxygen/shared/dist/otlp-log-sink.d.ts +79 -0
- package/node_modules/@oxygen/shared/dist/otlp-log-sink.js +366 -0
- package/node_modules/@oxygen/shared/dist/plan-capabilities.js +1 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +23 -22
- package/node_modules/@oxygen/shared/dist/plan-limits.js +45 -18
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +48 -41
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +36 -25
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +22 -22
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +40 -34
- package/node_modules/@oxygen/shared/dist/product-analytics-environment.js +9 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +36 -2
- package/node_modules/@oxygen/shared/dist/product-analytics-events.js +36 -1
- package/node_modules/@oxygen/shared/dist/rate-window.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/rate-window.js +8 -0
- package/node_modules/@oxygen/shared/dist/redaction.js +4 -1
- package/node_modules/@oxygen/shared/dist/research-output-contract.js +1 -3
- package/node_modules/@oxygen/shared/dist/scraper-lane-credential.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/scraper-lane-credential.js +23 -0
- package/node_modules/@oxygen/shared/dist/search-vocab.js +4 -5
- package/node_modules/@oxygen/shared/dist/select-options.js +6 -1
- package/node_modules/@oxygen/shared/dist/sequence-failures.js +1 -5
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +23 -0
- package/node_modules/@oxygen/shared/dist/sequences.js +115 -5
- package/node_modules/@oxygen/shared/dist/signup-lead-payload.d.ts +80 -0
- package/node_modules/@oxygen/shared/dist/signup-lead-payload.js +198 -0
- package/node_modules/@oxygen/shared/dist/social-capabilities.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/social-capabilities.js +25 -16
- package/node_modules/@oxygen/shared/dist/social-post-metrics-core.d.ts +32 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-core.js +32 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-linkedin.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-linkedin.js +103 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-series.d.ts +96 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-series.js +213 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-x.d.ts +13 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-x.js +78 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics.d.ts +36 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics.js +51 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +22 -10
- package/node_modules/@oxygen/shared/dist/spend-safety.js +15 -21
- package/node_modules/@oxygen/shared/dist/sql-rows.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/sql-rows.js +3 -0
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +36 -0
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +184 -0
- package/node_modules/@oxygen/shared/dist/stripe-subscription-kind.d.ts +41 -0
- package/node_modules/@oxygen/shared/dist/stripe-subscription-kind.js +44 -0
- package/node_modules/@oxygen/shared/dist/table-limits.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/table-limits.js +3 -0
- package/node_modules/@oxygen/shared/dist/telemetry-export-observer.d.ts +94 -0
- package/node_modules/@oxygen/shared/dist/telemetry-export-observer.js +298 -0
- package/node_modules/@oxygen/shared/dist/telemetry.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/telemetry.js +28 -2
- package/node_modules/@oxygen/shared/dist/type-guards.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/type-guards.js +35 -0
- package/node_modules/@oxygen/shared/dist/ugc.d.ts +22 -11
- package/node_modules/@oxygen/shared/dist/ugc.js +10 -0
- package/node_modules/@oxygen/shared/dist/value-readers.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/value-readers.js +59 -0
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/version.generated.js +2 -0
- package/node_modules/@oxygen/shared/dist/version.js +8 -1
- package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +0 -23
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +29 -0
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +31 -0
- package/node_modules/@oxygen/shared/package.json +59 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +2 -5
- package/node_modules/@oxygen/workflows/dist/graph/params.js +1 -1
- package/package.json +3 -2
|
@@ -42,7 +42,6 @@ export declare const PRODUCT_EVENTS: {
|
|
|
42
42
|
readonly FIRST_PLAY_STARTED: "first_play_started";
|
|
43
43
|
readonly FIRST_PLAY_STEP_COMPLETED: "first_play_step_completed";
|
|
44
44
|
readonly FIRST_PLAY_COMPLETED: "first_play_completed";
|
|
45
|
-
readonly FIRST_PLAY_SKIPPED: "first_play_skipped";
|
|
46
45
|
/**
|
|
47
46
|
* Usage — what was ASKED for. `mcp_tool_called` used to sit here as the
|
|
48
47
|
* second highest-volume event; it was REPLACED (not joined) by the reserved
|
|
@@ -70,13 +69,33 @@ export declare const PRODUCT_EVENTS: {
|
|
|
70
69
|
readonly TABLE_INGESTION_RUN_SETTLED: "table_ingestion_run_settled";
|
|
71
70
|
readonly AGENT_RUN_SETTLED: "agent_run_settled";
|
|
72
71
|
readonly SEQUENCE_ENROLLMENT_SETTLED: "sequence_enrollment_settled";
|
|
72
|
+
/**
|
|
73
|
+
* A Workspace Copilot turn reached completed / failed / cancelled. The
|
|
74
|
+
* Copilot is a Control surface, not a primitive, but its turns are durable
|
|
75
|
+
* worker runs like the others and it had no outcome event at all. Counts,
|
|
76
|
+
* credits and ids only — never the user message or the assistant text.
|
|
77
|
+
*/
|
|
78
|
+
readonly COPILOT_TURN_SETTLED: "copilot_turn_settled";
|
|
73
79
|
/** Revenue lifecycle. */
|
|
80
|
+
/**
|
|
81
|
+
* A Stripe Checkout was opened for a first subscription. `source` says which
|
|
82
|
+
* surface opened it (a paywall, the sidebar Upgrade row, Billing) and
|
|
83
|
+
* `blocked_capability` which wall, so signup → wall → checkout →
|
|
84
|
+
* `subscription_activated` is one funnel instead of a gap between pageviews.
|
|
85
|
+
*/
|
|
86
|
+
readonly CHECKOUT_STARTED: "checkout_started";
|
|
74
87
|
readonly TRIAL_STARTED: "trial_started";
|
|
75
88
|
readonly SUBSCRIPTION_ACTIVATED: "subscription_activated";
|
|
76
89
|
readonly SUBSCRIPTION_CHURNED: "subscription_churned";
|
|
77
90
|
readonly TRIAL_CANCEL_SCHEDULED: "trial_cancel_scheduled";
|
|
78
91
|
readonly TRIAL_CANCEL_RESUMED: "trial_cancel_resumed";
|
|
79
92
|
readonly TRIAL_ENDED_UNCONVERTED: "trial_ended_unconverted";
|
|
93
|
+
/**
|
|
94
|
+
* A paid credit top-up was granted (the Stripe Checkout fulfilment, after the
|
|
95
|
+
* ledger grant committed). `uuid` derives from the checkout session, so the
|
|
96
|
+
* webhook, the success redirect and every redelivery collapse to one event.
|
|
97
|
+
*/
|
|
98
|
+
readonly CREDITS_PURCHASED: "credits_purchased";
|
|
80
99
|
/**
|
|
81
100
|
* Marketing site. The public hero lookup on oxygen-agent.com: an ANONYMOUS
|
|
82
101
|
* visitor types their website and OXYGEN spends its own credits profiling it.
|
|
@@ -101,6 +120,21 @@ export declare const PRODUCT_EVENTS: {
|
|
|
101
120
|
readonly HERO_LOOKUP_SUBMITTED: "hero_lookup_submitted";
|
|
102
121
|
readonly HERO_LOOKUP_RENDERED: "hero_lookup_rendered";
|
|
103
122
|
readonly HERO_SIGNUP_CLICKED: "hero_signup_clicked";
|
|
123
|
+
/**
|
|
124
|
+
* Web navigation timing, measured in the browser from the click on an
|
|
125
|
+
* internal link (capture phase, before Next's own handler) to: the first
|
|
126
|
+
* visible feedback, the URL commit, and the destination's own readiness
|
|
127
|
+
* seam (`lib/analytics/navigation-readiness.ts`). Properties are route
|
|
128
|
+
* TEMPLATES (ids collapsed), a duration and enums — never a raw path, a
|
|
129
|
+
* query string or page content. PostHog owns these because they answer
|
|
130
|
+
* "how does the product feel per route per release", which is a usage
|
|
131
|
+
* question joinable to the funnel; a navigation that never became usable
|
|
132
|
+
* is a defect and is ALSO beaconed to Axiom as a client error.
|
|
133
|
+
*/
|
|
134
|
+
readonly WEB_NAVIGATION_FEEDBACK: "web_navigation_feedback";
|
|
135
|
+
readonly WEB_NAVIGATION_MEANINGFUL: "web_navigation_meaningful";
|
|
136
|
+
readonly WEB_NAVIGATION_USABLE: "web_navigation_usable";
|
|
137
|
+
readonly WEB_NAVIGATION_USABLE_TIMEOUT: "web_navigation_usable_timeout";
|
|
104
138
|
/** Pipeline heartbeat: proves ingestion is alive when the product is quiet. */
|
|
105
139
|
readonly POSTHOG_CANARY: "posthog_canary";
|
|
106
140
|
};
|
|
@@ -112,5 +146,5 @@ export declare const PRODUCT_EVENT_NAMES: readonly ProductEventName[];
|
|
|
112
146
|
* emitter cannot be handed a lifecycle event that only the control plane has
|
|
113
147
|
* the facts for.
|
|
114
148
|
*/
|
|
115
|
-
export declare const WORKER_RUN_OUTCOME_EVENTS: readonly ["workflow_run_settled", "table_action_run_settled", "table_ingestion_run_settled", "agent_run_settled", "sequence_enrollment_settled"];
|
|
149
|
+
export declare const WORKER_RUN_OUTCOME_EVENTS: readonly ["workflow_run_settled", "table_action_run_settled", "table_ingestion_run_settled", "agent_run_settled", "sequence_enrollment_settled", "copilot_turn_settled"];
|
|
116
150
|
export type WorkerRunOutcomeEventName = (typeof WORKER_RUN_OUTCOME_EVENTS)[number];
|
|
@@ -42,7 +42,6 @@ export const PRODUCT_EVENTS = {
|
|
|
42
42
|
FIRST_PLAY_STARTED: "first_play_started",
|
|
43
43
|
FIRST_PLAY_STEP_COMPLETED: "first_play_step_completed",
|
|
44
44
|
FIRST_PLAY_COMPLETED: "first_play_completed",
|
|
45
|
-
FIRST_PLAY_SKIPPED: "first_play_skipped",
|
|
46
45
|
/**
|
|
47
46
|
* Usage — what was ASKED for. `mcp_tool_called` used to sit here as the
|
|
48
47
|
* second highest-volume event; it was REPLACED (not joined) by the reserved
|
|
@@ -70,13 +69,33 @@ export const PRODUCT_EVENTS = {
|
|
|
70
69
|
TABLE_INGESTION_RUN_SETTLED: "table_ingestion_run_settled",
|
|
71
70
|
AGENT_RUN_SETTLED: "agent_run_settled",
|
|
72
71
|
SEQUENCE_ENROLLMENT_SETTLED: "sequence_enrollment_settled",
|
|
72
|
+
/**
|
|
73
|
+
* A Workspace Copilot turn reached completed / failed / cancelled. The
|
|
74
|
+
* Copilot is a Control surface, not a primitive, but its turns are durable
|
|
75
|
+
* worker runs like the others and it had no outcome event at all. Counts,
|
|
76
|
+
* credits and ids only — never the user message or the assistant text.
|
|
77
|
+
*/
|
|
78
|
+
COPILOT_TURN_SETTLED: "copilot_turn_settled",
|
|
73
79
|
/** Revenue lifecycle. */
|
|
80
|
+
/**
|
|
81
|
+
* A Stripe Checkout was opened for a first subscription. `source` says which
|
|
82
|
+
* surface opened it (a paywall, the sidebar Upgrade row, Billing) and
|
|
83
|
+
* `blocked_capability` which wall, so signup → wall → checkout →
|
|
84
|
+
* `subscription_activated` is one funnel instead of a gap between pageviews.
|
|
85
|
+
*/
|
|
86
|
+
CHECKOUT_STARTED: "checkout_started",
|
|
74
87
|
TRIAL_STARTED: "trial_started",
|
|
75
88
|
SUBSCRIPTION_ACTIVATED: "subscription_activated",
|
|
76
89
|
SUBSCRIPTION_CHURNED: "subscription_churned",
|
|
77
90
|
TRIAL_CANCEL_SCHEDULED: "trial_cancel_scheduled",
|
|
78
91
|
TRIAL_CANCEL_RESUMED: "trial_cancel_resumed",
|
|
79
92
|
TRIAL_ENDED_UNCONVERTED: "trial_ended_unconverted",
|
|
93
|
+
/**
|
|
94
|
+
* A paid credit top-up was granted (the Stripe Checkout fulfilment, after the
|
|
95
|
+
* ledger grant committed). `uuid` derives from the checkout session, so the
|
|
96
|
+
* webhook, the success redirect and every redelivery collapse to one event.
|
|
97
|
+
*/
|
|
98
|
+
CREDITS_PURCHASED: "credits_purchased",
|
|
80
99
|
/**
|
|
81
100
|
* Marketing site. The public hero lookup on oxygen-agent.com: an ANONYMOUS
|
|
82
101
|
* visitor types their website and OXYGEN spends its own credits profiling it.
|
|
@@ -101,6 +120,21 @@ export const PRODUCT_EVENTS = {
|
|
|
101
120
|
HERO_LOOKUP_SUBMITTED: "hero_lookup_submitted",
|
|
102
121
|
HERO_LOOKUP_RENDERED: "hero_lookup_rendered",
|
|
103
122
|
HERO_SIGNUP_CLICKED: "hero_signup_clicked",
|
|
123
|
+
/**
|
|
124
|
+
* Web navigation timing, measured in the browser from the click on an
|
|
125
|
+
* internal link (capture phase, before Next's own handler) to: the first
|
|
126
|
+
* visible feedback, the URL commit, and the destination's own readiness
|
|
127
|
+
* seam (`lib/analytics/navigation-readiness.ts`). Properties are route
|
|
128
|
+
* TEMPLATES (ids collapsed), a duration and enums — never a raw path, a
|
|
129
|
+
* query string or page content. PostHog owns these because they answer
|
|
130
|
+
* "how does the product feel per route per release", which is a usage
|
|
131
|
+
* question joinable to the funnel; a navigation that never became usable
|
|
132
|
+
* is a defect and is ALSO beaconed to Axiom as a client error.
|
|
133
|
+
*/
|
|
134
|
+
WEB_NAVIGATION_FEEDBACK: "web_navigation_feedback",
|
|
135
|
+
WEB_NAVIGATION_MEANINGFUL: "web_navigation_meaningful",
|
|
136
|
+
WEB_NAVIGATION_USABLE: "web_navigation_usable",
|
|
137
|
+
WEB_NAVIGATION_USABLE_TIMEOUT: "web_navigation_usable_timeout",
|
|
104
138
|
/** Pipeline heartbeat: proves ingestion is alive when the product is quiet. */
|
|
105
139
|
POSTHOG_CANARY: "posthog_canary",
|
|
106
140
|
};
|
|
@@ -117,4 +151,5 @@ export const WORKER_RUN_OUTCOME_EVENTS = Object.freeze([
|
|
|
117
151
|
PRODUCT_EVENTS.TABLE_INGESTION_RUN_SETTLED,
|
|
118
152
|
PRODUCT_EVENTS.AGENT_RUN_SETTLED,
|
|
119
153
|
PRODUCT_EVENTS.SEQUENCE_ENROLLMENT_SETTLED,
|
|
154
|
+
PRODUCT_EVENTS.COPILOT_TURN_SETTLED,
|
|
120
155
|
]);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed-window bucketing arithmetic shared by every rate/spend limiter.
|
|
3
|
+
*/
|
|
4
|
+
/** The start of the fixed `windowSeconds` window containing `now`. */
|
|
5
|
+
export function startOfWindow(now, windowSeconds) {
|
|
6
|
+
const windowMs = windowSeconds * 1000;
|
|
7
|
+
return new Date(Math.floor(now.getTime() / windowMs) * windowMs);
|
|
8
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const SECRET_KEY_PATTERN = /(api[_-]?key|x[_-]?key|authorization|bearer|cookie|password|secret|token|ciphertext|connection[_-]?string|connection[_-]?uri|database[_-]?url|dsn)/i;
|
|
2
|
-
|
|
2
|
+
// `content_preview` is raw model output (ai-column-runner's invalid_ai_output
|
|
3
|
+
// details); web routes log `error_details` on a 4xx, and model text belongs in
|
|
4
|
+
// Langfuse only (ADR 0014), never in operational logs.
|
|
5
|
+
const OMITTED_KEY_PATTERN = /^(body|payload|prompt|prompts|raw_prompt|raw_prompts|row|rows|input|inputs|output|outputs|request|response|provider_payload|provider_response|customer_data|content_preview)$/i;
|
|
3
6
|
// OXY-125: keyed redaction (SECRET_KEY_PATTERN) only catches whole fields named
|
|
4
7
|
// like a secret. Credentials also leak as *substrings* of otherwise-ordinary
|
|
5
8
|
// fields — most commonly an Authorization header dumped into error_message /
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRecord as isObject } from "./type-guards.js";
|
|
1
2
|
const MAX_RESEARCH_SECTIONS = 20;
|
|
2
3
|
const OUTPUT_SECTIONS_MARKER = /\b(?:return|provide|include|produce|output)\b.{0,80}\b(?:labeled|named|required|requested)?\s*(?:output\s+)?sections?\s*:/i;
|
|
3
4
|
const LABELED_SECTION = /^(?:[-*+]\s+|\d+[.)]\s+)?([A-Za-z][A-Za-z0-9_. /&()'’+\-]{1,79})\s*:\s*.*$/;
|
|
@@ -253,6 +254,3 @@ function hasExactMembers(value, expected) {
|
|
|
253
254
|
&& value.length === expected.length
|
|
254
255
|
&& expected.every((member) => value.includes(member));
|
|
255
256
|
}
|
|
256
|
-
function isObject(value) {
|
|
257
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
258
|
-
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which key a LinkedIn scraper lane runs on (Philipp, 2026-09-25). Every lane
|
|
3
|
+
* defaults to OXYGEN's managed credits; a workspace may instead run it on its own
|
|
4
|
+
* account with the lane's vendor, which OXYGEN then never bills. The vendor is a
|
|
5
|
+
* property of the lane, so a caller picks only the mode.
|
|
6
|
+
*
|
|
7
|
+
* Browser-safe on purpose: the New table lanes render the menu from this list.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SCRAPER_LANE_CREDENTIAL_MODES: readonly ["managed", "user_api_key"];
|
|
10
|
+
export type ScraperLaneCredentialMode = (typeof SCRAPER_LANE_CREDENTIAL_MODES)[number];
|
|
11
|
+
export type ScraperLane = "linkedin_posts" | "linkedin_post_engagers" | "linkedin_company_followers";
|
|
12
|
+
/** The integration that stores each lane's customer key, and its display name. */
|
|
13
|
+
export declare const SCRAPER_LANE_KEY_VENDOR: Readonly<Record<ScraperLane, {
|
|
14
|
+
integrationId: "up2data" | "scrapeli";
|
|
15
|
+
name: string;
|
|
16
|
+
}>>;
|
|
17
|
+
/** Absent means managed; anything outside the two modes is null, for the caller to refuse. */
|
|
18
|
+
export declare function readScraperLaneCredentialMode(value: unknown): ScraperLaneCredentialMode | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which key a LinkedIn scraper lane runs on (Philipp, 2026-09-25). Every lane
|
|
3
|
+
* defaults to OXYGEN's managed credits; a workspace may instead run it on its own
|
|
4
|
+
* account with the lane's vendor, which OXYGEN then never bills. The vendor is a
|
|
5
|
+
* property of the lane, so a caller picks only the mode.
|
|
6
|
+
*
|
|
7
|
+
* Browser-safe on purpose: the New table lanes render the menu from this list.
|
|
8
|
+
*/
|
|
9
|
+
export const SCRAPER_LANE_CREDENTIAL_MODES = ["managed", "user_api_key"];
|
|
10
|
+
/** The integration that stores each lane's customer key, and its display name. */
|
|
11
|
+
export const SCRAPER_LANE_KEY_VENDOR = {
|
|
12
|
+
linkedin_posts: { integrationId: "up2data", name: "Up2Data" },
|
|
13
|
+
linkedin_post_engagers: { integrationId: "up2data", name: "Up2Data" },
|
|
14
|
+
linkedin_company_followers: { integrationId: "scrapeli", name: "ScrapeLi" },
|
|
15
|
+
};
|
|
16
|
+
/** Absent means managed; anything outside the two modes is null, for the caller to refuse. */
|
|
17
|
+
export function readScraperLaneCredentialMode(value) {
|
|
18
|
+
if (value === undefined || value === null || value === "")
|
|
19
|
+
return "managed";
|
|
20
|
+
return SCRAPER_LANE_CREDENTIAL_MODES.includes(value)
|
|
21
|
+
? value
|
|
22
|
+
: null;
|
|
23
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// Shared search-planner vocabulary + stateless text helpers.
|
|
2
2
|
//
|
|
3
3
|
// Single source of truth for the constraint vocabulary and pure text helpers
|
|
4
|
-
// consumed by
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// here is what stops the two surfaces from silently drifting (OXY-414).
|
|
4
|
+
// consumed by the web search planners (apps/web/src/lib/company-search-plan.ts,
|
|
5
|
+
// people-search-plan.ts). Any other search classifier or compiler layers its
|
|
6
|
+
// tuning deltas on top of these bases rather than re-listing vocabulary, so the
|
|
7
|
+
// surfaces cannot silently drift (OXY-414).
|
|
9
8
|
//
|
|
10
9
|
// Everything here is a pure function or a static table: no Date.now, no
|
|
11
10
|
// randomness, no I/O — safe to import from any layer.
|
|
@@ -75,7 +75,12 @@ export const IMAGE_COLUMN_SEMANTIC = "image";
|
|
|
75
75
|
export function isImageColumnSemantic(semanticType) {
|
|
76
76
|
return semanticType === "image" || semanticType === "avatar" || semanticType === "photo";
|
|
77
77
|
}
|
|
78
|
-
|
|
78
|
+
// A sanity ceiling, not a correctness one: it stops a malformed write from
|
|
79
|
+
// turning one column into an unbounded list. Raised from 200 to 300 for the
|
|
80
|
+
// CRM's country column, whose vocabulary is the 250-entry LinkedIn country set
|
|
81
|
+
// (see @oxygen/shared/linkedin-countries) — at 200 the tail of the alphabet was
|
|
82
|
+
// silently truncated away, which reads as "Oxygen has never heard of Vietnam".
|
|
83
|
+
const MAX_OPTIONS = 300;
|
|
79
84
|
// Normalize a raw options array (from a column `definition.options`, CLI/MCP, or
|
|
80
85
|
// the editor) into canonical SelectOptions: dedupe by value, default colors,
|
|
81
86
|
// canonical 0..n ordering. Accepts strings or {value,label,color,order} objects.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { redactCustomerFacingDetails, redactCustomerFacingError, } from "./error-redaction.js";
|
|
2
2
|
import { isProviderFundingErrorCode, isProviderRateLimitErrorCode, } from "./provider-funding-errors.js";
|
|
3
|
+
import { asRecordOrNull as asRecord } from "./type-guards.js";
|
|
3
4
|
export const SEQUENCE_FAILURE_SCHEMA_VERSION = 1;
|
|
4
5
|
export const SEQUENCE_FAILURE_RETRY_AFTER_MAX_MS = 24 * 60 * 60 * 1000;
|
|
5
6
|
export const SEQUENCE_FAILURE_CLASSES = [
|
|
@@ -318,8 +319,3 @@ function firstBoolean(records, keys) {
|
|
|
318
319
|
}
|
|
319
320
|
return null;
|
|
320
321
|
}
|
|
321
|
-
function asRecord(value) {
|
|
322
|
-
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
323
|
-
? value
|
|
324
|
-
: null;
|
|
325
|
-
}
|
|
@@ -1176,4 +1176,27 @@ export declare function secondsUntilSendWindowEnd(window: SequenceSendWindow, no
|
|
|
1176
1176
|
* describes this window's sends.
|
|
1177
1177
|
*/
|
|
1178
1178
|
export declare function secondsSinceSendWindowStart(window: SequenceSendWindow, now: Date, timezone?: string): number | null;
|
|
1179
|
+
/**
|
|
1180
|
+
* The next instant at which `isWithinSendWindow` becomes true, or null when the
|
|
1181
|
+
* window is already open, never opens, or cannot be evaluated.
|
|
1182
|
+
*
|
|
1183
|
+
* This is what a deferral should wait for. Before it existed, the two
|
|
1184
|
+
* native-email send-window gates deferred a flat +30 minutes, so every pending
|
|
1185
|
+
* action was re-claimed, re-planned and re-written twice an hour for as long as
|
|
1186
|
+
* the window stayed shut — ~128 times each across a closed weekend, which is
|
|
1187
|
+
* where 24,261 of one fortnight's deferral log lines came from.
|
|
1188
|
+
*
|
|
1189
|
+
* Null on exactly the cases its three siblings decline: an unresolvable
|
|
1190
|
+
* timezone, a malformed `start`/`end`, and a zero-width window. The caller keeps
|
|
1191
|
+
* its own fallback delay for those, so a bad window can never park an action
|
|
1192
|
+
* forever — the same fail-open posture isWithinSendWindow takes.
|
|
1193
|
+
*
|
|
1194
|
+
* Computed by stepping to the next allowed local `start` and then correcting
|
|
1195
|
+
* against the real clock, because minutes-of-day arithmetic alone is wrong
|
|
1196
|
+
* across a DST boundary (the offset moves between `now` and the candidate). The
|
|
1197
|
+
* correction re-reads the candidate's local time and closes the gap; the result
|
|
1198
|
+
* is only returned once isWithinSendWindow itself agrees, so this function can
|
|
1199
|
+
* never hand back an instant the gate would reject.
|
|
1200
|
+
*/
|
|
1201
|
+
export declare function nextSendWindowOpenAt(window: SequenceSendWindow, now: Date, timezone?: string): Date | null;
|
|
1179
1202
|
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { OxygenError } from "./cli-result.js";
|
|
2
2
|
import { hashVariantKey, renderTemplate, spintaxSyntaxIssues, templateColumnKeys, } from "./sequence-template.js";
|
|
3
|
+
import { isRecord } from "./type-guards.js";
|
|
4
|
+
import { LINKEDIN_INVITE_NOTE_LIMIT } from "./linkedin-sequences.js";
|
|
3
5
|
/**
|
|
4
6
|
* Multichannel sequence DSL — the shared contract validated identically by CLI,
|
|
5
7
|
* MCP, API, and web. A sequence is an ordered list of steps applied to each
|
|
@@ -996,7 +998,10 @@ export const SEQUENCE_CRM_IDENTITY_KINDS = [
|
|
|
996
998
|
export const SEQUENCE_CONNECTION_BRANCH_CONDITIONS = ["connection_accepted", "already_connected", "open_profile"];
|
|
997
999
|
const MAX_SEQUENCE_STEPS = 50;
|
|
998
1000
|
const MAX_TEMPLATE_LENGTH = 8_000;
|
|
999
|
-
|
|
1001
|
+
// The Premium ceiling, and only an authoring hint: the limit that actually
|
|
1002
|
+
// binds is the SENDER's, which is lower on a Basic account and is enforced
|
|
1003
|
+
// against the RENDERED note at dispatch. See LINKEDIN_INVITE_NOTE_LIMIT_BASIC.
|
|
1004
|
+
const MAX_NOTE_LENGTH = LINKEDIN_INVITE_NOTE_LIMIT;
|
|
1000
1005
|
const MAX_CRM_TASK_PROPERTIES = 100;
|
|
1001
1006
|
const MAX_CRM_TASK_ASSOCIATIONS = 20;
|
|
1002
1007
|
const MAX_CRM_RECORD_MAPPINGS = 10;
|
|
@@ -2563,6 +2568,36 @@ function hhmmToMinutes(value) {
|
|
|
2563
2568
|
return hours * 60 + minutes;
|
|
2564
2569
|
}
|
|
2565
2570
|
const ISO_WEEKDAY = { Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6, Sun: 7 };
|
|
2571
|
+
/**
|
|
2572
|
+
* Does this window admit `isoWeekday`? Shared by every gate so they cannot
|
|
2573
|
+
* disagree about what a day list means.
|
|
2574
|
+
*
|
|
2575
|
+
* `days` is contractually ISO numbers (normalizeWeekdays rejects anything else),
|
|
2576
|
+
* but `settings.email_send_window` is stored without passing through it, and
|
|
2577
|
+
* production holds windows whose days are `["mon","tue",...]`. A raw
|
|
2578
|
+
* `days.includes(1)` is false for every day of the week, so such a window is
|
|
2579
|
+
* never open and its Sequence can never send — a silent, permanent stall that
|
|
2580
|
+
* also contradicts this module's fail-open posture for a malformed window.
|
|
2581
|
+
* Names are therefore accepted, and a list with no recognisable entry at all is
|
|
2582
|
+
* treated as absent (every day), which is exactly what normalizeWeekdays
|
|
2583
|
+
* produces for the same input.
|
|
2584
|
+
*/
|
|
2585
|
+
function sendWindowAllowsWeekday(days, isoWeekday) {
|
|
2586
|
+
let recognised = 0;
|
|
2587
|
+
for (const entry of days) {
|
|
2588
|
+
const day = typeof entry === "number"
|
|
2589
|
+
? entry
|
|
2590
|
+
: typeof entry === "string"
|
|
2591
|
+
? ISO_WEEKDAY[`${entry.trim().slice(0, 1).toUpperCase()}${entry.trim().slice(1, 3).toLowerCase()}`] ?? Number(entry)
|
|
2592
|
+
: Number.NaN;
|
|
2593
|
+
if (!Number.isInteger(day) || day < 1 || day > 7)
|
|
2594
|
+
continue;
|
|
2595
|
+
recognised += 1;
|
|
2596
|
+
if (day === isoWeekday)
|
|
2597
|
+
return true;
|
|
2598
|
+
}
|
|
2599
|
+
return recognised === 0;
|
|
2600
|
+
}
|
|
2566
2601
|
/**
|
|
2567
2602
|
* Resolve which IANA timezone a send window evaluates in for a given lead row.
|
|
2568
2603
|
* "recipient" mode reads the lead's tz from `recipient_timezone_column`, falling
|
|
@@ -2593,7 +2628,7 @@ export function isWithinSendWindow(window, now, timezone) {
|
|
|
2593
2628
|
const endM = hhmmToMinutes(window.end);
|
|
2594
2629
|
if (startM === null || endM === null)
|
|
2595
2630
|
return true; // malformed window → fail open
|
|
2596
|
-
if (window.days && window.days.length > 0 && !window.days
|
|
2631
|
+
if (window.days && window.days.length > 0 && !sendWindowAllowsWeekday(window.days, parts.isoWeekday))
|
|
2597
2632
|
return false;
|
|
2598
2633
|
if (startM === endM)
|
|
2599
2634
|
return false; // empty window
|
|
@@ -2654,6 +2689,84 @@ export function secondsSinceSendWindowStart(window, now, timezone) {
|
|
|
2654
2689
|
: minutes + 24 * 60 - startM; // wrapped past midnight: opened yesterday evening
|
|
2655
2690
|
return elapsedMinutes >= 0 ? elapsedMinutes * 60 : null;
|
|
2656
2691
|
}
|
|
2692
|
+
/**
|
|
2693
|
+
* How far ahead nextSendWindowOpenAt will look for an opening. A window with a
|
|
2694
|
+
* `days` list opens at least weekly, so 8 days covers every configurable gap
|
|
2695
|
+
* with a day of slack; anything that finds nothing inside it is a window that
|
|
2696
|
+
* never opens (zero-width, or a malformed tz that fails the parts read).
|
|
2697
|
+
*/
|
|
2698
|
+
const SEND_WINDOW_LOOKAHEAD_DAYS = 8;
|
|
2699
|
+
/**
|
|
2700
|
+
* The next instant at which `isWithinSendWindow` becomes true, or null when the
|
|
2701
|
+
* window is already open, never opens, or cannot be evaluated.
|
|
2702
|
+
*
|
|
2703
|
+
* This is what a deferral should wait for. Before it existed, the two
|
|
2704
|
+
* native-email send-window gates deferred a flat +30 minutes, so every pending
|
|
2705
|
+
* action was re-claimed, re-planned and re-written twice an hour for as long as
|
|
2706
|
+
* the window stayed shut — ~128 times each across a closed weekend, which is
|
|
2707
|
+
* where 24,261 of one fortnight's deferral log lines came from.
|
|
2708
|
+
*
|
|
2709
|
+
* Null on exactly the cases its three siblings decline: an unresolvable
|
|
2710
|
+
* timezone, a malformed `start`/`end`, and a zero-width window. The caller keeps
|
|
2711
|
+
* its own fallback delay for those, so a bad window can never park an action
|
|
2712
|
+
* forever — the same fail-open posture isWithinSendWindow takes.
|
|
2713
|
+
*
|
|
2714
|
+
* Computed by stepping to the next allowed local `start` and then correcting
|
|
2715
|
+
* against the real clock, because minutes-of-day arithmetic alone is wrong
|
|
2716
|
+
* across a DST boundary (the offset moves between `now` and the candidate). The
|
|
2717
|
+
* correction re-reads the candidate's local time and closes the gap; the result
|
|
2718
|
+
* is only returned once isWithinSendWindow itself agrees, so this function can
|
|
2719
|
+
* never hand back an instant the gate would reject.
|
|
2720
|
+
*/
|
|
2721
|
+
export function nextSendWindowOpenAt(window, now, timezone) {
|
|
2722
|
+
const tz = timezone?.trim() ? timezone.trim() : window.timezone;
|
|
2723
|
+
const parts = tzParts(now, tz) ?? tzParts(now, window.timezone) ?? tzParts(now, "UTC");
|
|
2724
|
+
if (!parts)
|
|
2725
|
+
return null;
|
|
2726
|
+
const startM = hhmmToMinutes(window.start);
|
|
2727
|
+
const endM = hhmmToMinutes(window.end);
|
|
2728
|
+
if (startM === null || endM === null || startM === endM)
|
|
2729
|
+
return null;
|
|
2730
|
+
if (isWithinSendWindow(window, now, timezone))
|
|
2731
|
+
return null;
|
|
2732
|
+
const dayAllowed = (isoWeekday) => !window.days || window.days.length === 0 || sendWindowAllowsWeekday(window.days, isoWeekday);
|
|
2733
|
+
const minutes = parts.hour * 60 + parts.minute;
|
|
2734
|
+
// Days to skip before the window's local `start` is both in the future and on
|
|
2735
|
+
// an allowed weekday. 0 means "later today".
|
|
2736
|
+
let dayOffset = minutes < startM && dayAllowed(parts.isoWeekday) ? 0 : 1;
|
|
2737
|
+
while (dayOffset <= SEND_WINDOW_LOOKAHEAD_DAYS && !dayAllowed(((parts.isoWeekday - 1 + dayOffset) % 7) + 1)) {
|
|
2738
|
+
dayOffset += 1;
|
|
2739
|
+
}
|
|
2740
|
+
if (dayOffset > SEND_WINDOW_LOOKAHEAD_DAYS)
|
|
2741
|
+
return null;
|
|
2742
|
+
let candidate = new Date(now.getTime() + ((dayOffset * 24 * 60) + startM - minutes) * 60_000);
|
|
2743
|
+
// DST correction: the offset that applied at `now` may not apply at the
|
|
2744
|
+
// candidate, so the naive arithmetic can land an hour early or late. Re-read
|
|
2745
|
+
// the candidate's own local time and close the gap. Two rounds are enough for
|
|
2746
|
+
// any real transition (the second only runs if the first lands on the shifted
|
|
2747
|
+
// side of the boundary); a zone with no transition converges on the first.
|
|
2748
|
+
for (let round = 0; round < 2; round += 1) {
|
|
2749
|
+
const at = tzParts(candidate, tz);
|
|
2750
|
+
if (!at)
|
|
2751
|
+
break;
|
|
2752
|
+
const drift = (at.hour * 60 + at.minute) - startM;
|
|
2753
|
+
if (drift === 0)
|
|
2754
|
+
break;
|
|
2755
|
+
// Choose the nearer direction so a ±23h reading across midnight (the window
|
|
2756
|
+
// opens just after a backward transition) corrects by minutes, not a day.
|
|
2757
|
+
const shift = drift > 12 * 60 ? drift - 24 * 60 : drift < -12 * 60 ? drift + 24 * 60 : drift;
|
|
2758
|
+
candidate = new Date(candidate.getTime() - shift * 60_000);
|
|
2759
|
+
}
|
|
2760
|
+
// A local `start` can be skipped entirely by a forward DST jump, and a
|
|
2761
|
+
// corrected candidate can land a minute on the wrong side. Walk forward to the
|
|
2762
|
+
// first minute the gate actually accepts rather than trusting the arithmetic.
|
|
2763
|
+
for (let nudge = 0; nudge <= 120; nudge += 1) {
|
|
2764
|
+
const at = new Date(candidate.getTime() + nudge * 60_000);
|
|
2765
|
+
if (at.getTime() > now.getTime() && isWithinSendWindow(window, at, timezone))
|
|
2766
|
+
return at;
|
|
2767
|
+
}
|
|
2768
|
+
return null;
|
|
2769
|
+
}
|
|
2657
2770
|
function tzParts(now, tz) {
|
|
2658
2771
|
try {
|
|
2659
2772
|
const formatted = new Intl.DateTimeFormat("en-US", {
|
|
@@ -2852,6 +2965,3 @@ function normalizeWeekdays(value, path, issues) {
|
|
|
2852
2965
|
}
|
|
2853
2966
|
return out.length > 0 ? out.sort((a, b) => a - b) : undefined;
|
|
2854
2967
|
}
|
|
2855
|
-
function isRecord(value) {
|
|
2856
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2857
|
-
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type JsonRecord = Record<string, unknown>;
|
|
2
|
+
export type SignupLeadWebhookSource = "clerk" | "onboarding" | "backfill" | "stripe";
|
|
3
|
+
export type CustomerLifecycleEvent = "signup"
|
|
4
|
+
/** Creator research completed by the onboarding bootstrap; produced by @oxygen/control-db, never by Clerk. */
|
|
5
|
+
| "signup_research" | "trial_created" | "paid" | "churned";
|
|
6
|
+
export type SignupLeadWebhookInput = {
|
|
7
|
+
eventId?: string | null | undefined;
|
|
8
|
+
providerEventId?: string | null | undefined;
|
|
9
|
+
type: string;
|
|
10
|
+
source: SignupLeadWebhookSource;
|
|
11
|
+
lifecycleEvent?: CustomerLifecycleEvent | null | undefined;
|
|
12
|
+
occurredAt?: Date | string | number | null | undefined;
|
|
13
|
+
user?: {
|
|
14
|
+
clerkUserId?: string | null;
|
|
15
|
+
email?: string | null;
|
|
16
|
+
firstName?: string | null;
|
|
17
|
+
lastName?: string | null;
|
|
18
|
+
createdAt?: Date | string | number | null | undefined;
|
|
19
|
+
/** A profile URL the provider already knows for this person; the consent-recorded creator URL still wins at enqueue. */
|
|
20
|
+
linkedinUrl?: string | null;
|
|
21
|
+
};
|
|
22
|
+
organization?: {
|
|
23
|
+
clerkOrgId?: string | null;
|
|
24
|
+
name?: string | null;
|
|
25
|
+
slug?: string | null;
|
|
26
|
+
domain?: string | null;
|
|
27
|
+
createdAt?: Date | string | number | null | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Clerk user id of the organization's creator (Clerk `organization.created_by`).
|
|
30
|
+
* Downstream qualification only treats the exact creator as an acquisition
|
|
31
|
+
* lead; invited members get onboarding but no founder outreach.
|
|
32
|
+
*/
|
|
33
|
+
createdBy?: string | null;
|
|
34
|
+
};
|
|
35
|
+
membership?: {
|
|
36
|
+
role?: string | null;
|
|
37
|
+
};
|
|
38
|
+
stripe?: {
|
|
39
|
+
customerId?: string | null;
|
|
40
|
+
subscriptionId?: string | null;
|
|
41
|
+
invoiceId?: string | null;
|
|
42
|
+
priceId?: string | null;
|
|
43
|
+
tier?: string | null;
|
|
44
|
+
status?: string | null;
|
|
45
|
+
trialStart?: Date | string | number | null | undefined;
|
|
46
|
+
trialEnd?: Date | string | number | null | undefined;
|
|
47
|
+
periodStart?: Date | string | number | null | undefined;
|
|
48
|
+
periodEnd?: Date | string | number | null | undefined;
|
|
49
|
+
amountPaid?: number | null;
|
|
50
|
+
currency?: string | null;
|
|
51
|
+
billingReason?: string | null;
|
|
52
|
+
};
|
|
53
|
+
metadata?: JsonRecord;
|
|
54
|
+
/** Allow-listed research evidence (`person_facts` / `company_facts`) when the producer already holds it. */
|
|
55
|
+
qualificationEvidence?: JsonRecord;
|
|
56
|
+
/** Research intent or status attached to the fact (`expected`, `scope`, `run_id`, ...). */
|
|
57
|
+
research?: JsonRecord;
|
|
58
|
+
rawEvent?: JsonRecord;
|
|
59
|
+
};
|
|
60
|
+
export type SignupLeadWebhookPayload = JsonRecord & {
|
|
61
|
+
id: string;
|
|
62
|
+
event_id: string;
|
|
63
|
+
event: string;
|
|
64
|
+
type: string;
|
|
65
|
+
occurred_at: string;
|
|
66
|
+
source: SignupLeadWebhookSource;
|
|
67
|
+
event_type: string;
|
|
68
|
+
provider_event_id: string;
|
|
69
|
+
};
|
|
70
|
+
export type WorkEmailClassification = {
|
|
71
|
+
domain: string | null;
|
|
72
|
+
isGenericMailbox: boolean;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Classifies an email address into a (likely) company work-email domain.
|
|
76
|
+
* Consumer/generic mailboxes return `{ domain: null, isGenericMailbox: true }`.
|
|
77
|
+
*/
|
|
78
|
+
export declare function classifyWorkEmail(email: string | null | undefined): WorkEmailClassification;
|
|
79
|
+
export declare function isGenericMailboxDomain(domain: string | null | undefined): boolean;
|
|
80
|
+
export declare function buildSignupLeadWebhookPayload(input: SignupLeadWebhookInput): SignupLeadWebhookPayload;
|