@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
@@ -0,0 +1,548 @@
1
+ // The company-enrichment FIELD CATALOG: every field the company waterfall can
2
+ // resolve, with the vocabulary every surface shows for it.
3
+ //
4
+ // This is the single source of truth for the field list. The waterfall engine
5
+ // (packages/integrations/src/company-enrichment-waterfall.ts) owns HOW a field
6
+ // is resolved — provider lanes, derived extractors, pricing — and reads its
7
+ // labels, column keys and data types from here; the CLI, the MCP descriptor,
8
+ // the `company_enrich` column preset (tenant-db) and the web column picker
9
+ // read the same catalog, so a field cannot exist on one surface and not
10
+ // another. Three hand-copied 8-entry enums drifted independently before this
11
+ // module existed (route, MCP schema, engine), and only the engine's was tested.
12
+ //
13
+ // CLIENT-SAFE LEAF: exported as the `@oxygen/shared/company-enrichment-fields`
14
+ // subpath and imported by browser components through the tenant-db preset
15
+ // module. It must never gain a runtime import.
16
+ export const COMPANY_ENRICHMENT_FIELD_CATEGORIES = [
17
+ "identity",
18
+ "firmographics",
19
+ "funding",
20
+ "relationships",
21
+ "signals",
22
+ "technology",
23
+ "web_presence",
24
+ ];
25
+ export const COMPANY_ENRICHMENT_FIELD_CATEGORY_LABELS = {
26
+ identity: "Identity",
27
+ firmographics: "Firmographics",
28
+ funding: "Funding",
29
+ relationships: "Relationships",
30
+ signals: "Signals",
31
+ technology: "Technology",
32
+ web_presence: "Web presence",
33
+ };
34
+ const PROFILE_THEN_FUNDING = [
35
+ { field: "company_profile", autoRun: true },
36
+ { field: "funding", autoRun: false },
37
+ ];
38
+ export const COMPANY_ENRICHMENT_FIELD_DEFINITIONS = [
39
+ // ── identity ─────────────────────────────────────────────────────────────
40
+ {
41
+ field: "domain",
42
+ label: "Company domain",
43
+ category: "identity",
44
+ description: "The company's website domain, resolved from its name or LinkedIn page.",
45
+ columnKey: "company_domain",
46
+ dataType: "text",
47
+ semanticType: "company.domain",
48
+ derivedFrom: [],
49
+ },
50
+ {
51
+ field: "linkedin_url",
52
+ label: "Company LinkedIn URL",
53
+ category: "identity",
54
+ description: "The company's LinkedIn page, resolved from its domain or name.",
55
+ columnKey: "company_linkedin_url",
56
+ dataType: "text",
57
+ semanticType: "company.linkedin_url",
58
+ derivedFrom: [],
59
+ },
60
+ // ── firmographics ────────────────────────────────────────────────────────
61
+ {
62
+ field: "headcount",
63
+ label: "Headcount",
64
+ category: "firmographics",
65
+ description: "Employee count, exact when the provider has it, otherwise the size band.",
66
+ columnKey: "company_headcount",
67
+ dataType: "text",
68
+ semanticType: "company.employee_count",
69
+ derivedFrom: [],
70
+ },
71
+ {
72
+ field: "industry",
73
+ label: "Industry",
74
+ category: "firmographics",
75
+ description: "The industry the company lists for itself.",
76
+ columnKey: "company_industry",
77
+ dataType: "text",
78
+ semanticType: "company.industry",
79
+ derivedFrom: [],
80
+ },
81
+ {
82
+ field: "company_profile",
83
+ label: "Company profile",
84
+ category: "firmographics",
85
+ description: "The whole provider profile as one record; the free fields below are read out of it.",
86
+ columnKey: "company_profile",
87
+ dataType: "jsonb",
88
+ semanticType: null,
89
+ derivedFrom: [],
90
+ },
91
+ {
92
+ field: "description",
93
+ label: "Description",
94
+ category: "firmographics",
95
+ description: "The company's own about text, read from the profile already fetched.",
96
+ columnKey: "company_description",
97
+ dataType: "text",
98
+ semanticType: "company.description",
99
+ derivedFrom: PROFILE_THEN_FUNDING,
100
+ },
101
+ {
102
+ field: "founded_year",
103
+ label: "Founded year",
104
+ category: "firmographics",
105
+ description: "The year the company was founded, read from the profile already fetched.",
106
+ columnKey: "company_founded_year",
107
+ dataType: "numeric",
108
+ semanticType: "company.founded_year",
109
+ derivedFrom: PROFILE_THEN_FUNDING,
110
+ },
111
+ {
112
+ field: "hq_address",
113
+ label: "HQ address",
114
+ category: "firmographics",
115
+ description: "Headquarters street, city and country as one line, from the profile already fetched.",
116
+ columnKey: "company_hq_address",
117
+ dataType: "text",
118
+ semanticType: "company.hq_location",
119
+ derivedFrom: PROFILE_THEN_FUNDING,
120
+ },
121
+ {
122
+ field: "hq_country",
123
+ label: "HQ country",
124
+ category: "firmographics",
125
+ description: "Headquarters country, from the profile already fetched.",
126
+ columnKey: "company_hq_country",
127
+ dataType: "text",
128
+ semanticType: "company.hq_country",
129
+ derivedFrom: PROFILE_THEN_FUNDING,
130
+ },
131
+ {
132
+ field: "company_type",
133
+ label: "Company type",
134
+ category: "firmographics",
135
+ description: "Privately held, public, non-profit and the like, from the profile already fetched.",
136
+ columnKey: "company_type",
137
+ dataType: "text",
138
+ semanticType: null,
139
+ derivedFrom: PROFILE_THEN_FUNDING,
140
+ },
141
+ {
142
+ field: "specialties",
143
+ label: "Specialties",
144
+ category: "firmographics",
145
+ description: "The specialties or keywords the company lists, from the profile already fetched.",
146
+ columnKey: "company_specialties",
147
+ dataType: "jsonb",
148
+ semanticType: "company.specialties",
149
+ derivedFrom: [{ field: "company_profile", autoRun: true }],
150
+ },
151
+ // ── funding ──────────────────────────────────────────────────────────────
152
+ {
153
+ field: "funding",
154
+ label: "Funding",
155
+ category: "funding",
156
+ description: "The raw funding record from a funding provider: rounds, amounts, investors, total.",
157
+ columnKey: "company_funding",
158
+ dataType: "jsonb",
159
+ semanticType: null,
160
+ derivedFrom: [],
161
+ },
162
+ {
163
+ field: "revenue",
164
+ label: "Revenue (estimate)",
165
+ category: "funding",
166
+ description: "Estimated annual revenue, a figure when the source has one, otherwise a range band.",
167
+ columnKey: "company_revenue",
168
+ dataType: "text",
169
+ semanticType: "company.revenue",
170
+ derivedFrom: [],
171
+ },
172
+ {
173
+ field: "funding_stage",
174
+ label: "Funding stage",
175
+ category: "funding",
176
+ description: "The latest round's type, e.g. Seed or Series B; free when the LinkedIn page lists funding.",
177
+ columnKey: "company_funding_stage",
178
+ dataType: "text",
179
+ semanticType: null,
180
+ derivedFrom: PROFILE_THEN_FUNDING,
181
+ },
182
+ {
183
+ field: "latest_funding_round",
184
+ label: "Latest funding round",
185
+ category: "funding",
186
+ description: "Date, amount, round type and lead investors of the most recent round.",
187
+ columnKey: "company_latest_funding_round",
188
+ dataType: "jsonb",
189
+ semanticType: null,
190
+ derivedFrom: PROFILE_THEN_FUNDING,
191
+ },
192
+ {
193
+ field: "total_funding",
194
+ label: "Total funding",
195
+ category: "funding",
196
+ description: "Total raised to date in USD, from a funding provider (LinkedIn does not publish it).",
197
+ columnKey: "company_total_funding",
198
+ dataType: "numeric",
199
+ semanticType: "company.funding_raised",
200
+ derivedFrom: [
201
+ { field: "funding", autoRun: true },
202
+ { field: "company_profile", autoRun: false },
203
+ ],
204
+ },
205
+ {
206
+ field: "funding_rounds_count",
207
+ label: "Number of funding rounds",
208
+ category: "funding",
209
+ description: "How many rounds the company has raised.",
210
+ columnKey: "company_funding_rounds_count",
211
+ dataType: "numeric",
212
+ semanticType: null,
213
+ derivedFrom: PROFILE_THEN_FUNDING,
214
+ },
215
+ {
216
+ field: "investors",
217
+ label: "Investors",
218
+ category: "funding",
219
+ description: "Named investors across the rounds a provider knows about.",
220
+ columnKey: "company_investors",
221
+ dataType: "jsonb",
222
+ semanticType: null,
223
+ derivedFrom: PROFILE_THEN_FUNDING,
224
+ },
225
+ // ── relationships ────────────────────────────────────────────────────────
226
+ {
227
+ field: "competitors",
228
+ label: "Competitors",
229
+ category: "relationships",
230
+ description: "Named competitors from a funding-intelligence provider; LinkedIn's similar pages when the profile is already on the row.",
231
+ columnKey: "company_competitors",
232
+ dataType: "jsonb",
233
+ semanticType: null,
234
+ derivedFrom: [
235
+ { field: "funding", autoRun: false },
236
+ { field: "company_profile", autoRun: false },
237
+ ],
238
+ availability: "coming_soon",
239
+ },
240
+ {
241
+ field: "corporate_structure",
242
+ label: "Corporate structure",
243
+ category: "relationships",
244
+ description: "Parent company and subsidiaries as one record.",
245
+ columnKey: "company_corporate_structure",
246
+ dataType: "jsonb",
247
+ semanticType: null,
248
+ derivedFrom: [],
249
+ availability: "coming_soon",
250
+ },
251
+ {
252
+ field: "parent_company",
253
+ label: "Parent company",
254
+ category: "relationships",
255
+ description: "The company that owns this one, read from the corporate structure lookup.",
256
+ columnKey: "company_parent_company",
257
+ dataType: "text",
258
+ semanticType: null,
259
+ derivedFrom: [{ field: "corporate_structure", autoRun: true }],
260
+ availability: "coming_soon",
261
+ },
262
+ {
263
+ field: "subsidiaries",
264
+ label: "Subsidiaries",
265
+ category: "relationships",
266
+ description: "Companies this one owns, read from the corporate structure lookup.",
267
+ columnKey: "company_subsidiaries",
268
+ dataType: "jsonb",
269
+ semanticType: null,
270
+ derivedFrom: [{ field: "corporate_structure", autoRun: true }],
271
+ availability: "coming_soon",
272
+ },
273
+ {
274
+ field: "acquisitions",
275
+ label: "Acquisitions",
276
+ category: "relationships",
277
+ description: "Companies this one has acquired, with dates and sources.",
278
+ columnKey: "company_acquisitions",
279
+ dataType: "jsonb",
280
+ semanticType: null,
281
+ derivedFrom: [{ field: "funding", autoRun: false }],
282
+ availability: "coming_soon",
283
+ },
284
+ // ── signals ──────────────────────────────────────────────────────────────
285
+ {
286
+ field: "news",
287
+ label: "News",
288
+ category: "signals",
289
+ description: "Recent dated news items about the company.",
290
+ columnKey: "company_news",
291
+ dataType: "jsonb",
292
+ semanticType: null,
293
+ derivedFrom: [{ field: "funding", autoRun: false }],
294
+ availability: "coming_soon",
295
+ },
296
+ {
297
+ field: "hiring_signals",
298
+ label: "Hiring signals",
299
+ category: "signals",
300
+ description: "The raw open-roles payload from a jobs provider.",
301
+ columnKey: "company_hiring_signals",
302
+ dataType: "jsonb",
303
+ semanticType: null,
304
+ derivedFrom: [],
305
+ },
306
+ {
307
+ field: "job_openings",
308
+ label: "Job openings",
309
+ category: "signals",
310
+ description: "Open roles from the hiring lookup, listed (up to ten) with a count.",
311
+ columnKey: "company_job_openings",
312
+ dataType: "jsonb",
313
+ semanticType: null,
314
+ derivedFrom: [{ field: "hiring_signals", autoRun: true }],
315
+ },
316
+ // ── technology ───────────────────────────────────────────────────────────
317
+ {
318
+ field: "technologies",
319
+ label: "Tech stack",
320
+ category: "technology",
321
+ description: "Technologies the company runs, from its job postings (with confidence and first/last-seen dates) or its website.",
322
+ columnKey: "company_technologies",
323
+ dataType: "jsonb",
324
+ semanticType: null,
325
+ derivedFrom: [],
326
+ },
327
+ {
328
+ field: "technology_check",
329
+ label: "Technology check",
330
+ category: "technology",
331
+ description: "Yes or no per technology you name, checked against the detected stack.",
332
+ columnKey: "company_technology_check",
333
+ dataType: "jsonb",
334
+ semanticType: null,
335
+ derivedFrom: [{ field: "technologies", autoRun: true }],
336
+ requires: "check_technologies",
337
+ },
338
+ // ── web presence ─────────────────────────────────────────────────────────
339
+ {
340
+ field: "logo_url",
341
+ label: "Logo",
342
+ category: "web_presence",
343
+ description: "The company logo image URL, from the profile already fetched.",
344
+ columnKey: "company_logo_url",
345
+ dataType: "text",
346
+ semanticType: "company.logo_url",
347
+ derivedFrom: [{ field: "company_profile", autoRun: true }],
348
+ },
349
+ {
350
+ field: "crunchbase_url",
351
+ label: "Crunchbase URL",
352
+ category: "web_presence",
353
+ description: "The company's Crunchbase page; free when the LinkedIn page links it, otherwise one web search.",
354
+ columnKey: "company_crunchbase_url",
355
+ dataType: "text",
356
+ semanticType: null,
357
+ derivedFrom: [
358
+ { field: "company_profile", autoRun: false },
359
+ { field: "funding", autoRun: false },
360
+ ],
361
+ availability: "coming_soon",
362
+ },
363
+ {
364
+ field: "github_url",
365
+ label: "GitHub URL",
366
+ category: "web_presence",
367
+ description: "The company's GitHub organization, found by one site-scoped web search.",
368
+ columnKey: "company_github_url",
369
+ dataType: "text",
370
+ semanticType: null,
371
+ derivedFrom: [],
372
+ availability: "coming_soon",
373
+ },
374
+ {
375
+ field: "facebook_url",
376
+ label: "Facebook URL",
377
+ category: "web_presence",
378
+ description: "The company's Facebook page, found by one site-scoped web search.",
379
+ columnKey: "company_facebook_url",
380
+ dataType: "text",
381
+ semanticType: "company.facebook_url",
382
+ derivedFrom: [{ field: "funding", autoRun: false }],
383
+ availability: "coming_soon",
384
+ },
385
+ {
386
+ field: "instagram_url",
387
+ label: "Instagram URL",
388
+ category: "web_presence",
389
+ description: "The company's Instagram profile, found by one site-scoped web search.",
390
+ columnKey: "company_instagram_url",
391
+ dataType: "text",
392
+ semanticType: "company.instagram_url",
393
+ derivedFrom: [],
394
+ availability: "coming_soon",
395
+ },
396
+ {
397
+ field: "youtube_url",
398
+ label: "YouTube URL",
399
+ category: "web_presence",
400
+ description: "The company's YouTube channel, found by one site-scoped web search.",
401
+ columnKey: "company_youtube_url",
402
+ dataType: "text",
403
+ semanticType: null,
404
+ derivedFrom: [],
405
+ availability: "coming_soon",
406
+ },
407
+ ];
408
+ export const COMPANY_ENRICHMENT_FIELD_KEYS = COMPANY_ENRICHMENT_FIELD_DEFINITIONS.map((definition) => definition.field);
409
+ const DEFINITIONS_BY_FIELD = new Map(COMPANY_ENRICHMENT_FIELD_DEFINITIONS.map((definition) => [definition.field, definition]));
410
+ export function isCompanyEnrichmentFieldKey(value) {
411
+ return DEFINITIONS_BY_FIELD.has(value);
412
+ }
413
+ export function companyEnrichmentFieldDefinition(field) {
414
+ const definition = DEFINITIONS_BY_FIELD.get(field);
415
+ if (!definition)
416
+ throw new Error(`Unknown company enrichment field "${field}".`);
417
+ return definition;
418
+ }
419
+ /** D51: a field parked `coming_soon` has no lane, no picker group and is refused by `--fields`. */
420
+ export function isComingSoonCompanyEnrichmentField(field) {
421
+ return companyEnrichmentFieldDefinition(field).availability === "coming_soon";
422
+ }
423
+ /** A field whose value is read out of another field's payload, never a provider call of its own. */
424
+ export function isDerivedCompanyEnrichmentField(field) {
425
+ return companyEnrichmentFieldDefinition(field).derivedFrom.length > 0;
426
+ }
427
+ /**
428
+ * What `find company` resolves when the caller names no fields. Identity plus
429
+ * the firmographics that cost nothing beyond the one profile lookup the
430
+ * headcount/industry lanes already make; funding, relationship and web-presence
431
+ * fields stay opt-in because each adds a priced lane.
432
+ */
433
+ export const DEFAULT_COMPANY_FIND_FIELDS = [
434
+ "domain",
435
+ "linkedin_url",
436
+ "headcount",
437
+ "industry",
438
+ "description",
439
+ "founded_year",
440
+ "hq_country",
441
+ ];
442
+ /**
443
+ * What `columns add --preset company_enrich` creates when the caller names no
444
+ * fields: the identity/firmographic set plus the full profile and what it says
445
+ * about the company. Every lane-priced relationship, funding, signal,
446
+ * technology and web-presence field is opt-in through `--fields`, so the
447
+ * default bundle's per-row ceiling stays the profile chain's.
448
+ *
449
+ * Funding stage and latest round left the default on 2026-09-24 (founder, D55):
450
+ * the profile lane is Blitz first now, and Blitz's company profile carries no
451
+ * funding, so on the rows Blitz answers those two columns would sit empty. They
452
+ * live in the Funding details group instead.
453
+ */
454
+ export const DEFAULT_COMPANY_PRESET_FIELDS = [
455
+ "domain",
456
+ "linkedin_url",
457
+ "headcount",
458
+ "industry",
459
+ "company_profile",
460
+ "description",
461
+ "founded_year",
462
+ "hq_country",
463
+ ];
464
+ /**
465
+ * The opt-in company fields, grouped the way a customer asks for them ("add
466
+ * competitors", "add their tech stack"). Every surface that offers a company
467
+ * field beyond the default bundle reads THIS list: the enrichment column catalog
468
+ * (`oxygen enrichment catalog`, `oxygen_enrichment_catalog`), the web add-column
469
+ * picker, and the catalog pricing. Adding a group adds its fields to the SAME
470
+ * company enrichment column — re-applying the bundle widens the existing
471
+ * column's field list instead of creating a second paid column.
472
+ */
473
+ export const COMPANY_ENRICHMENT_FIELD_GROUPS = [
474
+ {
475
+ id: "profile_details",
476
+ label: "Company profile details",
477
+ description: "HQ address, company type, specialties and logo, read from the company profile the bundle already fetches.",
478
+ fields: ["hq_address", "company_type", "specialties", "logo_url"],
479
+ cost: "included",
480
+ },
481
+ {
482
+ id: "funding_details",
483
+ label: "Funding details",
484
+ description: "Funding stage, latest round, total raised, number of rounds and investors, with the full funding history kept alongside.",
485
+ fields: ["funding", "revenue", "funding_stage", "latest_funding_round", "total_funding", "funding_rounds_count", "investors"],
486
+ cost: "extra",
487
+ },
488
+ {
489
+ id: "job_openings",
490
+ label: "Job openings",
491
+ description: "The roles each account is hiring for, as a count and a list.",
492
+ fields: ["hiring_signals", "job_openings"],
493
+ cost: "extra",
494
+ },
495
+ {
496
+ id: "tech_stack",
497
+ label: "Tech stack",
498
+ description: "The technologies each account runs, from its job postings (with confidence and first/last seen) or its website.",
499
+ fields: ["technologies"],
500
+ cost: "extra",
501
+ },
502
+ {
503
+ id: "technology_check",
504
+ label: "Technology check",
505
+ description: "Yes or no per account for each technology you name, such as HubSpot or Salesforce.",
506
+ fields: ["technology_check"],
507
+ cost: "extra",
508
+ requires: "check_technologies",
509
+ },
510
+ ];
511
+ export function companyEnrichmentFieldGroup(id) {
512
+ return COMPANY_ENRICHMENT_FIELD_GROUPS.find((group) => group.id === id) ?? null;
513
+ }
514
+ /**
515
+ * Parse a comma-separated or array field list against the catalog. Returns the
516
+ * unique, lowercased keys in request order, or throws naming every unknown
517
+ * entry with the supported list — never a silent fallback to a default set,
518
+ * which is how `--fields tech,hiring,profile` once ran the four default fields
519
+ * and billed for an answer nobody asked for (OXP-7.2.9).
520
+ */
521
+ export function parseCompanyEnrichmentFieldList(value) {
522
+ const entries = Array.isArray(value)
523
+ ? value
524
+ : typeof value === "string"
525
+ ? value.split(",")
526
+ : [];
527
+ const cleaned = entries
528
+ .map((entry) => (typeof entry === "string" ? entry.trim().toLowerCase() : ""))
529
+ .filter((entry) => entry.length > 0);
530
+ const fields = [];
531
+ const unknown = [];
532
+ const coming_soon = [];
533
+ for (const entry of cleaned) {
534
+ if (isCompanyEnrichmentFieldKey(entry)) {
535
+ if (isComingSoonCompanyEnrichmentField(entry)) {
536
+ if (!coming_soon.includes(entry))
537
+ coming_soon.push(entry);
538
+ }
539
+ else if (!fields.includes(entry)) {
540
+ fields.push(entry);
541
+ }
542
+ }
543
+ else if (!unknown.includes(entry)) {
544
+ unknown.push(entry);
545
+ }
546
+ }
547
+ return { fields, unknown, coming_soon };
548
+ }