@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.
Files changed (176) hide show
  1. package/README.md +1 -1
  2. package/dist/admin-primary-providers-render.d.ts +0 -2
  3. package/dist/admin-primary-providers-render.js +1 -1
  4. package/dist/browser-login.js +1 -4
  5. package/dist/column-decision-options.d.ts +20 -0
  6. package/dist/column-decision-options.js +54 -0
  7. package/dist/command-manifest.d.ts +3 -2
  8. package/dist/command-manifest.js +25 -2
  9. package/dist/credentials.d.ts +1 -1
  10. package/dist/functions-commands.js +13 -9
  11. package/dist/help.d.ts +8 -0
  12. package/dist/help.js +46 -0
  13. package/dist/index.js +2751 -242
  14. package/dist/knowledge-mirror.d.ts +2 -2
  15. package/dist/runtime.d.ts +0 -15
  16. package/dist/runtime.js +1 -1
  17. package/dist/search-ai-filter-notice.d.ts +17 -0
  18. package/dist/search-ai-filter-notice.js +38 -0
  19. package/dist/session.d.ts +4 -3
  20. package/dist/skills.d.ts +8 -7
  21. package/dist/skills.js +58 -20
  22. package/dist/transcript.d.ts +2 -1
  23. package/dist/util.d.ts +10 -1
  24. package/dist/util.js +14 -2
  25. package/node_modules/@oxygen/cli-ugc/dist/commands.js +296 -140
  26. package/node_modules/@oxygen/cli-ugc/dist/field-parser.d.ts +9 -0
  27. package/node_modules/@oxygen/cli-ugc/dist/field-parser.js +34 -0
  28. package/node_modules/@oxygen/formula/dist/coerce.d.ts +10 -0
  29. package/node_modules/@oxygen/formula/dist/coerce.js +10 -0
  30. package/node_modules/@oxygen/formula/dist/formula-functions.js +65 -0
  31. package/node_modules/@oxygen/formula/dist/hash.d.ts +19 -0
  32. package/node_modules/@oxygen/formula/dist/hash.js +199 -0
  33. package/node_modules/@oxygen/formula/dist/value-cleaners.d.ts +6 -1
  34. package/node_modules/@oxygen/formula/dist/value-cleaners.js +10 -26
  35. package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +13 -0
  36. package/node_modules/@oxygen/shared/dist/array-utils.d.ts +5 -0
  37. package/node_modules/@oxygen/shared/dist/array-utils.js +11 -0
  38. package/node_modules/@oxygen/shared/dist/billing.d.ts +99 -22
  39. package/node_modules/@oxygen/shared/dist/billing.js +195 -40
  40. package/node_modules/@oxygen/shared/dist/byok-connect.js +5 -0
  41. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +27 -0
  42. package/node_modules/@oxygen/shared/dist/capability-discovery.js +311 -28
  43. package/node_modules/@oxygen/shared/dist/cli-http-error.d.ts +8 -0
  44. package/node_modules/@oxygen/shared/dist/cli-http-error.js +8 -0
  45. package/node_modules/@oxygen/shared/dist/cli-result.js +1 -0
  46. package/node_modules/@oxygen/shared/dist/column-autofill.d.ts +52 -0
  47. package/node_modules/@oxygen/shared/dist/column-autofill.js +62 -0
  48. package/node_modules/@oxygen/shared/dist/column-decision.d.ts +50 -0
  49. package/node_modules/@oxygen/shared/dist/column-decision.js +228 -0
  50. package/node_modules/@oxygen/shared/dist/column-output-fields.js +2 -6
  51. package/node_modules/@oxygen/shared/dist/company-enrichment-fields.d.ts +113 -0
  52. package/node_modules/@oxygen/shared/dist/company-enrichment-fields.js +548 -0
  53. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +6 -6
  54. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +6 -6
  55. package/node_modules/@oxygen/shared/dist/cutover-freeze.d.ts +26 -0
  56. package/node_modules/@oxygen/shared/dist/cutover-freeze.js +52 -0
  57. package/node_modules/@oxygen/shared/dist/data-suppliers.d.ts +57 -0
  58. package/node_modules/@oxygen/shared/dist/data-suppliers.js +59 -0
  59. package/node_modules/@oxygen/shared/dist/deploy-env.d.ts +74 -0
  60. package/node_modules/@oxygen/shared/dist/deploy-env.js +82 -0
  61. package/node_modules/@oxygen/shared/dist/dnc-rules.d.ts +130 -0
  62. package/node_modules/@oxygen/shared/dist/dnc-rules.js +221 -0
  63. package/node_modules/@oxygen/shared/dist/enrichment-intents.d.ts +107 -0
  64. package/node_modules/@oxygen/shared/dist/enrichment-intents.js +809 -0
  65. package/node_modules/@oxygen/shared/dist/error-message.d.ts +1 -0
  66. package/node_modules/@oxygen/shared/dist/error-message.js +3 -0
  67. package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -3
  68. package/node_modules/@oxygen/shared/dist/external-write-policy.d.ts +33 -0
  69. package/node_modules/@oxygen/shared/dist/external-write-policy.js +68 -0
  70. package/node_modules/@oxygen/shared/dist/format-percent.d.ts +8 -0
  71. package/node_modules/@oxygen/shared/dist/format-percent.js +13 -0
  72. package/node_modules/@oxygen/shared/dist/freemail-domains.d.ts +81 -0
  73. package/node_modules/@oxygen/shared/dist/freemail-domains.js +157 -0
  74. package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +1 -0
  75. package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +1 -1
  76. package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +60 -4
  77. package/node_modules/@oxygen/shared/dist/hosted-ai.js +125 -10
  78. package/node_modules/@oxygen/shared/dist/index.d.ts +15 -0
  79. package/node_modules/@oxygen/shared/dist/index.js +15 -0
  80. package/node_modules/@oxygen/shared/dist/json-path.js +1 -3
  81. package/node_modules/@oxygen/shared/dist/knowledge-bases.js +1 -3
  82. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +2 -2
  83. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +2 -2
  84. package/node_modules/@oxygen/shared/dist/langfuse.d.ts +44 -1
  85. package/node_modules/@oxygen/shared/dist/langfuse.js +416 -14
  86. package/node_modules/@oxygen/shared/dist/linkedin-countries.d.ts +33 -0
  87. package/node_modules/@oxygen/shared/dist/linkedin-countries.js +361 -0
  88. package/node_modules/@oxygen/shared/dist/linkedin-country-timezones.d.ts +24 -0
  89. package/node_modules/@oxygen/shared/dist/linkedin-country-timezones.js +276 -0
  90. package/node_modules/@oxygen/shared/dist/linkedin-message-deletion.d.ts +2 -0
  91. package/node_modules/@oxygen/shared/dist/linkedin-message-deletion.js +5 -0
  92. package/node_modules/@oxygen/shared/dist/linkedin-post-keywords.d.ts +44 -0
  93. package/node_modules/@oxygen/shared/dist/linkedin-post-keywords.js +116 -0
  94. package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +96 -0
  95. package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +123 -0
  96. package/node_modules/@oxygen/shared/dist/llm-durable-capture.d.ts +24 -0
  97. package/node_modules/@oxygen/shared/dist/llm-durable-capture.js +89 -0
  98. package/node_modules/@oxygen/shared/dist/llm-prompts.d.ts +75 -0
  99. package/node_modules/@oxygen/shared/dist/llm-prompts.js +161 -0
  100. package/node_modules/@oxygen/shared/dist/log-sink-selector.d.ts +39 -0
  101. package/node_modules/@oxygen/shared/dist/log-sink-selector.js +56 -0
  102. package/node_modules/@oxygen/shared/dist/log.d.ts +1 -0
  103. package/node_modules/@oxygen/shared/dist/log.js +6 -1
  104. package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.d.ts +90 -0
  105. package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.js +130 -0
  106. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +17 -0
  107. package/node_modules/@oxygen/shared/dist/object-storage.js +21 -0
  108. package/node_modules/@oxygen/shared/dist/operational-telemetry.d.ts +24 -0
  109. package/node_modules/@oxygen/shared/dist/operational-telemetry.js +73 -0
  110. package/node_modules/@oxygen/shared/dist/otlp-log-sink.d.ts +79 -0
  111. package/node_modules/@oxygen/shared/dist/otlp-log-sink.js +366 -0
  112. package/node_modules/@oxygen/shared/dist/plan-capabilities.js +1 -0
  113. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +23 -22
  114. package/node_modules/@oxygen/shared/dist/plan-limits.js +45 -18
  115. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +48 -41
  116. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +36 -25
  117. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +22 -22
  118. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +40 -34
  119. package/node_modules/@oxygen/shared/dist/product-analytics-environment.js +9 -0
  120. package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +36 -2
  121. package/node_modules/@oxygen/shared/dist/product-analytics-events.js +36 -1
  122. package/node_modules/@oxygen/shared/dist/rate-window.d.ts +5 -0
  123. package/node_modules/@oxygen/shared/dist/rate-window.js +8 -0
  124. package/node_modules/@oxygen/shared/dist/redaction.js +4 -1
  125. package/node_modules/@oxygen/shared/dist/research-output-contract.js +1 -3
  126. package/node_modules/@oxygen/shared/dist/scraper-lane-credential.d.ts +18 -0
  127. package/node_modules/@oxygen/shared/dist/scraper-lane-credential.js +23 -0
  128. package/node_modules/@oxygen/shared/dist/search-vocab.js +4 -5
  129. package/node_modules/@oxygen/shared/dist/select-options.js +6 -1
  130. package/node_modules/@oxygen/shared/dist/sequence-failures.js +1 -5
  131. package/node_modules/@oxygen/shared/dist/sequences.d.ts +23 -0
  132. package/node_modules/@oxygen/shared/dist/sequences.js +115 -5
  133. package/node_modules/@oxygen/shared/dist/signup-lead-payload.d.ts +80 -0
  134. package/node_modules/@oxygen/shared/dist/signup-lead-payload.js +198 -0
  135. package/node_modules/@oxygen/shared/dist/social-capabilities.d.ts +6 -0
  136. package/node_modules/@oxygen/shared/dist/social-capabilities.js +25 -16
  137. package/node_modules/@oxygen/shared/dist/social-post-metrics-core.d.ts +32 -0
  138. package/node_modules/@oxygen/shared/dist/social-post-metrics-core.js +32 -0
  139. package/node_modules/@oxygen/shared/dist/social-post-metrics-linkedin.d.ts +31 -0
  140. package/node_modules/@oxygen/shared/dist/social-post-metrics-linkedin.js +103 -0
  141. package/node_modules/@oxygen/shared/dist/social-post-metrics-series.d.ts +96 -0
  142. package/node_modules/@oxygen/shared/dist/social-post-metrics-series.js +213 -0
  143. package/node_modules/@oxygen/shared/dist/social-post-metrics-x.d.ts +13 -0
  144. package/node_modules/@oxygen/shared/dist/social-post-metrics-x.js +78 -0
  145. package/node_modules/@oxygen/shared/dist/social-post-metrics.d.ts +36 -0
  146. package/node_modules/@oxygen/shared/dist/social-post-metrics.js +51 -0
  147. package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +22 -10
  148. package/node_modules/@oxygen/shared/dist/spend-safety.js +15 -21
  149. package/node_modules/@oxygen/shared/dist/sql-rows.d.ts +1 -0
  150. package/node_modules/@oxygen/shared/dist/sql-rows.js +3 -0
  151. package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +36 -0
  152. package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +184 -0
  153. package/node_modules/@oxygen/shared/dist/stripe-subscription-kind.d.ts +41 -0
  154. package/node_modules/@oxygen/shared/dist/stripe-subscription-kind.js +44 -0
  155. package/node_modules/@oxygen/shared/dist/table-limits.d.ts +3 -0
  156. package/node_modules/@oxygen/shared/dist/table-limits.js +3 -0
  157. package/node_modules/@oxygen/shared/dist/telemetry-export-observer.d.ts +94 -0
  158. package/node_modules/@oxygen/shared/dist/telemetry-export-observer.js +298 -0
  159. package/node_modules/@oxygen/shared/dist/telemetry.d.ts +11 -0
  160. package/node_modules/@oxygen/shared/dist/telemetry.js +28 -2
  161. package/node_modules/@oxygen/shared/dist/type-guards.d.ts +22 -0
  162. package/node_modules/@oxygen/shared/dist/type-guards.js +35 -0
  163. package/node_modules/@oxygen/shared/dist/ugc.d.ts +22 -11
  164. package/node_modules/@oxygen/shared/dist/ugc.js +10 -0
  165. package/node_modules/@oxygen/shared/dist/value-readers.d.ts +21 -0
  166. package/node_modules/@oxygen/shared/dist/value-readers.js +59 -0
  167. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -0
  168. package/node_modules/@oxygen/shared/dist/version.generated.js +2 -0
  169. package/node_modules/@oxygen/shared/dist/version.js +8 -1
  170. package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +0 -23
  171. package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +29 -0
  172. package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +31 -0
  173. package/node_modules/@oxygen/shared/package.json +59 -0
  174. package/node_modules/@oxygen/workflows/dist/graph/expression.js +2 -5
  175. package/node_modules/@oxygen/workflows/dist/graph/params.js +1 -1
  176. 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,5 @@
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 declare function startOfWindow(now: Date, windowSeconds: number): Date;
@@ -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
- 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)$/i;
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 BOTH the legacy web search planners
5
- // (apps/web/src/lib/company-search-plan.ts, people-search-plan.ts) and the
6
- // @oxygen/routing classifier/compiler (via packages/routing/src/vocab.ts,
7
- // which layers its named tuning deltas on top of these bases). Keeping one copy
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
- const MAX_OPTIONS = 200;
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
- const MAX_NOTE_LENGTH = 300;
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.includes(parts.isoWeekday))
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;