@oxygen-agent/cli 1.982.3 → 1.1003.12
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/command-manifest.d.ts +3 -2
- package/dist/command-manifest.js +10 -0
- package/dist/credentials.d.ts +1 -1
- package/dist/functions-commands.js +27 -7
- package/dist/help.d.ts +29 -0
- package/dist/help.js +139 -0
- package/dist/index.js +1875 -164
- package/dist/knowledge-mirror.d.ts +2 -2
- package/dist/runtime.d.ts +0 -15
- package/dist/runtime.js +1 -1
- package/dist/session.d.ts +4 -3
- package/dist/skills.d.ts +8 -7
- package/dist/skills.js +24 -10
- package/dist/transcript.d.ts +2 -1
- package/dist/ugc-commands.d.ts +3 -6
- package/dist/ugc-commands.js +2 -1200
- package/dist/util.d.ts +1 -1
- package/dist/util.js +1 -3
- package/node_modules/@oxygen/cli-ugc/dist/commands.d.ts +3 -0
- package/node_modules/@oxygen/cli-ugc/dist/commands.js +1178 -0
- package/node_modules/@oxygen/cli-ugc/dist/field-parser.d.ts +7 -0
- package/node_modules/@oxygen/cli-ugc/dist/field-parser.js +25 -0
- package/node_modules/@oxygen/cli-ugc/dist/index.d.ts +14 -0
- package/node_modules/@oxygen/cli-ugc/dist/index.js +5 -0
- package/node_modules/@oxygen/cli-ugc/package.json +15 -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/expression.js +14 -1
- package/node_modules/@oxygen/formula/dist/formula-functions.js +136 -1
- 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/index.d.ts +1 -0
- package/node_modules/@oxygen/formula/dist/index.js +1 -0
- package/node_modules/@oxygen/formula/dist/value-cleaners.d.ts +74 -0
- package/node_modules/@oxygen/formula/dist/value-cleaners.js +358 -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 +103 -47
- package/node_modules/@oxygen/shared/dist/billing.js +150 -40
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +114 -16
- package/node_modules/@oxygen/shared/dist/column-autofill.d.ts +52 -0
- package/node_modules/@oxygen/shared/dist/column-autofill.js +80 -0
- package/node_modules/@oxygen/shared/dist/column-output-fields.js +14 -10
- package/node_modules/@oxygen/shared/dist/company-enrichment-fields.d.ts +108 -0
- package/node_modules/@oxygen/shared/dist/company-enrichment-fields.js +545 -0
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.js +43 -0
- package/node_modules/@oxygen/shared/dist/copilot-skills.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +41 -0
- package/node_modules/@oxygen/shared/dist/copilot-skills.js +6 -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 +103 -0
- package/node_modules/@oxygen/shared/dist/enrichment-intents.js +819 -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/index.d.ts +14 -0
- package/node_modules/@oxygen/shared/dist/index.js +14 -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 +17 -2
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +28 -6
- package/node_modules/@oxygen/shared/dist/langfuse.d.ts +12 -1
- package/node_modules/@oxygen/shared/dist/langfuse.js +57 -8
- package/node_modules/@oxygen/shared/dist/linkedin-countries.d.ts +32 -0
- package/node_modules/@oxygen/shared/dist/linkedin-countries.js +359 -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/object-storage.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +21 -0
- package/node_modules/@oxygen/shared/dist/otlp-log-sink.d.ts +54 -0
- package/node_modules/@oxygen/shared/dist/otlp-log-sink.js +213 -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 +15 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.js +15 -0
- 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/research-output-contract.d.ts +33 -1
- package/node_modules/@oxygen/shared/dist/research-output-contract.js +65 -5
- 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-crm-events.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/sequence-failures.js +1 -5
- package/node_modules/@oxygen/shared/dist/sequence-hubspot-sync.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +49 -0
- package/node_modules/@oxygen/shared/dist/sequences.js +134 -4
- 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/telemetry.js +9 -1
- 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/value-readers.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/value-readers.js +59 -0
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/package.json +60 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +2 -5
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +15 -15
- package/node_modules/@oxygen/workflows/dist/graph/params.js +1 -1
- package/package.json +6 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type EnrichmentIntentTaxonomyEntry } from "./enrichment-intents.js";
|
|
1
2
|
import { type RecipePrimitive } from "./recipes.js";
|
|
2
3
|
export type CapabilityLayer = "Control" | "Knowledge" | "Data" | "Action" | "External";
|
|
3
4
|
export type CapabilityPosture = "read_only" | "workspace_write" | "paid_read" | "external_write" | "mixed";
|
|
@@ -29,6 +30,22 @@ export type CapabilityRouteMatch = {
|
|
|
29
30
|
* intent is that narrow; `recommendedCommands[0]` is the same command.
|
|
30
31
|
*/
|
|
31
32
|
exactCommand?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The opinionated enrichment catalogue row a data-type ask resolves to
|
|
35
|
+
* ("get the funding round" -> enrich_company:funding). Present only when
|
|
36
|
+
* the ask names a data type; the catalogue command is then the first
|
|
37
|
+
* recommendation, ahead of any raw provider tool.
|
|
38
|
+
*/
|
|
39
|
+
catalogIntent?: {
|
|
40
|
+
id: string;
|
|
41
|
+
label: string;
|
|
42
|
+
status: EnrichmentIntentTaxonomyEntry["status"];
|
|
43
|
+
group: EnrichmentIntentTaxonomyEntry["group"];
|
|
44
|
+
/** The catalogue-first path: read the row, then add/preview; `exactCommand` names the row. */
|
|
45
|
+
tools: string[];
|
|
46
|
+
commands: string[];
|
|
47
|
+
exactCommand: string;
|
|
48
|
+
};
|
|
32
49
|
};
|
|
33
50
|
export type PrimitiveRouteMatch = CapabilityRouteMatch & {
|
|
34
51
|
card: PrimitiveRouteCard;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { findEnrichmentIntentByAlias } from "./enrichment-intents.js";
|
|
1
2
|
import { RECIPE_PRIMITIVES } from "./recipes.js";
|
|
2
3
|
// One compact semantic index covers every top-level /api/cli endpoint section.
|
|
3
4
|
// It is navigation, never execution: live schemas, prices, readiness, and
|
|
@@ -210,10 +211,10 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
210
211
|
primitive: "tables",
|
|
211
212
|
owns: "Typed working datasets, rows, formulas, AI/tool/waterfall columns, reusable Functions with isolated drafts and published versions, cell state, projects, and run provenance.",
|
|
212
213
|
notFor: "Canonical CRM truth, message cadence, or an off-platform spreadsheet runtime.",
|
|
213
|
-
execution: "Create and run work in hosted OXYGEN Tables; validate a small sample before bounded paid runs. For standard person or company enrichment, `columns add <table> --preset person_enrich|company_enrich` (MCP oxygen_columns_add with preset) adds the maintained bundle in one call before any hand-built tool column.",
|
|
214
|
+
execution: "Create and run work in hosted OXYGEN Tables; validate a small sample before bounded paid runs. To see what a table's own columns can be enriched with and what each costs per row before adding anything, run `tools search --for-table <table>` (MCP oxygen_tools_search with for_table): free, it returns priced suggestions with the exact add command. For standard person or company enrichment, `columns add <table> --preset person_enrich|company_enrich` (MCP oxygen_columns_add with preset) adds the maintained bundle in one call before any hand-built tool column. Person questions over that bundle's profile payload (skill set, grad school, location, job fit, current company) and person appearance research (events, keynotes, podcasts, GitHub profile) are `columns catalog --category people` templates, added with `columns add <table> --prompt-key <key> --input <name>=<column>`. For a company fact OXYGEN already knows how to research (founders, parent company, funding, cloud provider, offers demos, industry, NAICS, HQ, LinkedIn/Crunchbase page lookups) or a page extraction, `columns catalog` lists the ready-made template and `columns add <table> --prompt-key <key> --input <name>=<column>` (MCP oxygen_columns_add with prompt_key) adds it with no prompt to write.",
|
|
214
215
|
posture: "mixed",
|
|
215
|
-
gatewayTools: ["oxygen_tables_create", "oxygen_columns_add", "oxygen_enrich_column_preview", "oxygen_tables_link_bulk", "oxygen_callables_manage"],
|
|
216
|
-
gatewayCommands: ["tables create", "columns add", "enrich-column preview", "tables link", "functions list", "functions draft"],
|
|
216
|
+
gatewayTools: ["oxygen_tables_create", "oxygen_tools_search", "oxygen_columns_add", "oxygen_columns_catalog", "oxygen_enrich_column_preview", "oxygen_tables_link_bulk", "oxygen_callables_manage"],
|
|
217
|
+
gatewayCommands: ["tables create", "tools search", "columns add", "columns catalog", "enrich-column preview", "tables link", "functions list", "functions draft"],
|
|
217
218
|
skills: ["oxygen-gtm", "oxygen-table-tidy", "oxygen-diagnostics", "oxygen-clay-migration", "oxygen-linkedin-marketing"],
|
|
218
219
|
endpointSections: ["action-columns", "callables", "functions", "columns", "company-enrichment", "enrich-column", "enrichment", "projects", "supabase", "table-action-items", "table-action-runs", "table-ingestion-runs", "tables"],
|
|
219
220
|
// "link"/"join"/"connect"/"relate" route here for `tables link`. Added after a
|
|
@@ -221,7 +222,12 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
221
222
|
// `tables create` / `columns add` / `enrich-column preview` — none of which
|
|
222
223
|
// do it. The agent only found the right command by grepping the raw 25k-line
|
|
223
224
|
// command manifest, which is not a discovery path a customer has.
|
|
224
|
-
|
|
225
|
+
// "founders" / "parent company" / "cloud provider" / "company research" route
|
|
226
|
+
// here for `columns catalog`. Added after the 2026-09-16 blind baseline asked
|
|
227
|
+
// `capabilities search "founders of a company"` and got `route: null` with a
|
|
228
|
+
// generic sourcing pointer, then hand-wrote four research prompts for
|
|
229
|
+
// questions the template catalog now answers by key.
|
|
230
|
+
intentTerms: ["table", "spreadsheet", "rows", "column", "columns", "dataset", "csv", "import", "enrich", "enrichment", "waterfall", "score", "formula", "ai column", "lookup", "link", "link tables", "join", "connect", "relate", "relationship", "function", "functions", "reusable function", "function draft", "function version", "callable", "column template", "skill set", "grad school", "podcast appearance", "keynote", "github profile", "job fit", "founders", "parent company", "subsidiaries", "cloud provider", "company research", "research question", "research column", "web research", "template catalog", "ready-made", "offers demos", "naics", "cost per row", "price per row", "what can i enrich", "enrichment cost", "credits per row"],
|
|
225
231
|
},
|
|
226
232
|
{
|
|
227
233
|
id: "messages",
|
|
@@ -269,15 +275,26 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
269
275
|
id: "dashboards",
|
|
270
276
|
layer: "Data",
|
|
271
277
|
primitive: "dashboards",
|
|
272
|
-
owns: "
|
|
273
|
-
notFor: "Arbitrary BI modeling or a separate analytics store.",
|
|
274
|
-
execution: "Compute hosted curated views over Records, Messages, Signals, Sequences
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
owns: "The curated funnel, channel, spend and needs-action reporting, PLUS the workspace's own saved dashboards: named pages of chart / view / iframe / rich-text / code / live-source widgets over tables and CRM objects.",
|
|
279
|
+
notFor: "Arbitrary BI modeling or a separate analytics store. Outreach volume and replies are not chartable yet — group a table or a CRM object, or read the curated funnel with `dashboards summary`.",
|
|
280
|
+
execution: "Compute hosted curated views over Records, Messages, Signals, Sequences and runs; store and serve user-defined dashboards from ox_dashboards.",
|
|
281
|
+
// Was `read_only` until v1.1002.0, which is when a workspace could first SAVE a
|
|
282
|
+
// dashboard. A blind user eval (2026-09-21) caught the drift the expensive way:
|
|
283
|
+
// the agent asked this router for "dashboard", was told read_only and
|
|
284
|
+
// "not for BI modeling", and only found the eight mutating commands by
|
|
285
|
+
// separately searching the command index. This record is step 4 of the CLI's
|
|
286
|
+
// own quickstart, so a stale posture here routes users away from what shipped.
|
|
287
|
+
// `mixed`, not workspace_write: this capability now spans a read (summary,
|
|
288
|
+
// widget data), a workspace write (dashboards, tabs, widgets) AND one
|
|
289
|
+
// external-reach spending call (a live_source refresh calls its bound
|
|
290
|
+
// provider). "read_write" is not a member of CapabilityPosture -- inventing
|
|
291
|
+
// it is what broke the shared build on the first attempt.
|
|
292
|
+
posture: "mixed",
|
|
293
|
+
gatewayTools: ["oxygen_dashboard_summary", "oxygen_dashboards_list", "oxygen_dashboards_create", "oxygen_dashboards_widget_kinds", "oxygen_dashboards_widget_add", "oxygen_dashboards_widget_data"],
|
|
294
|
+
gatewayCommands: ["dashboards summary", "dashboards list", "dashboards create", "dashboards widgets kinds", "dashboards widgets add", "dashboards widgets data"],
|
|
278
295
|
skills: ["oxygen-gtm"],
|
|
279
296
|
endpointSections: ["dashboards"],
|
|
280
|
-
intentTerms: ["dashboard", "report", "reporting", "funnel", "metrics", "kpi", "performance", "pipeline summary"],
|
|
297
|
+
intentTerms: ["dashboard", "report", "reporting", "funnel", "metrics", "kpi", "performance", "pipeline summary", "save", "saved dashboard", "custom dashboard", "widget", "chart", "weekly report", "monday", "come back to"],
|
|
281
298
|
},
|
|
282
299
|
{
|
|
283
300
|
id: "ads",
|
|
@@ -313,10 +330,15 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
313
330
|
primitive: "sequences",
|
|
314
331
|
owns: "Net-new LinkedIn outreach plus versioned multichannel programs, cadence, enrollment, sender limits, suppression, reply-stop, and dispatch.",
|
|
315
332
|
notFor: "A reply in an existing conversation or a direct one-off email; use Messages. Not for general deterministic automation.",
|
|
316
|
-
|
|
333
|
+
// The external-campaign sentence exists because a 2026-09-16 blind user asked
|
|
334
|
+
// to "push table rows to smartlead campaign", landed here, read the native
|
|
335
|
+
// email track's "Only 'instantly' is supported" and concluded Smartlead was
|
|
336
|
+
// unreachable. The governed path for a campaign the customer already runs
|
|
337
|
+
// elsewhere is `sequences push-external`; it is not a Sequence.
|
|
338
|
+
execution: "Use the one-step gateway for new LinkedIn initiation or create/enroll/start a hosted campaign with bounded send authority. To push table rows into an existing Smartlead, Instantly, lemlist or HeyReach campaign you already run, use `sequences push-external` (dry_run preview, then --approved); the native email track of a Sequence stays Instantly-bound.",
|
|
317
339
|
posture: "external_write",
|
|
318
|
-
gatewayTools: ["oxygen_sequences_send", "oxygen_sequences_create", "oxygen_sequences_enroll", "oxygen_sequences_start", "oxygen_sequences_list"],
|
|
319
|
-
gatewayCommands: ["sequences send", "sequences create", "sequences enroll", "sequences start", "sequences list"],
|
|
340
|
+
gatewayTools: ["oxygen_sequences_send", "oxygen_sequences_create", "oxygen_sequences_enroll", "oxygen_sequences_start", "oxygen_sequences_list", "oxygen_sequences_push_external"],
|
|
341
|
+
gatewayCommands: ["sequences send", "sequences create", "sequences enroll", "sequences start", "sequences list", "sequences push-external"],
|
|
320
342
|
skills: ["oxygen-sequencer"],
|
|
321
343
|
endpointSections: ["senders", "sequencer", "sequences", "spintax", "suppressions", "voice"],
|
|
322
344
|
// "sender identity", "sender profile" and "phone number" are here because a
|
|
@@ -324,7 +346,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
324
346
|
// to workspace-access — "identity" matched the AUTH sense. The nouns a user
|
|
325
347
|
// reaches for when naming a sending identity or the dialing pool have to land
|
|
326
348
|
// on the group that actually owns `senders profiles` and `voice numbers`.
|
|
327
|
-
intentTerms: ["sequence", "campaign", "cadence", "enroll", "outreach", "linkedin message", "linkedin dm", "nurture", "follow up", "sender rotation", "stop on reply", "sender identity", "sending identity", "sender profile", "phone number", "dialing", "dialer", "call", "cold call"],
|
|
349
|
+
intentTerms: ["sequence", "campaign", "cadence", "enroll", "outreach", "linkedin message", "linkedin dm", "nurture", "follow up", "sender rotation", "stop on reply", "sender identity", "sending identity", "sender profile", "phone number", "dialing", "dialer", "call", "cold call", "smartlead", "instantly", "lemlist", "heyreach", "external sequencer", "push to campaign", "push rows to campaign", "add leads to campaign"],
|
|
328
350
|
},
|
|
329
351
|
{
|
|
330
352
|
id: "publishing",
|
|
@@ -493,6 +515,8 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
493
515
|
export const OXYGEN_PRIMITIVE_ROUTES = OXYGEN_CAPABILITY_ROUTES
|
|
494
516
|
.filter((card) => card.primitive !== null);
|
|
495
517
|
const ROUTE_BY_ID = new Map(OXYGEN_CAPABILITY_ROUTES.map((card) => [card.id, card]));
|
|
518
|
+
/** Cards a catalogue row may decorate: the data surfaces, never sequences/posts/billing. */
|
|
519
|
+
const CATALOGUE_CARD_IDS = new Set(["tables", "sourcing-and-provider-tools", "signals"]);
|
|
496
520
|
const ROUTE_BY_PRIMITIVE = new Map(OXYGEN_PRIMITIVE_ROUTES.map((card) => [card.primitive, card]));
|
|
497
521
|
const NORMALIZED_PRIMITIVES = [...RECIPE_PRIMITIVES].sort();
|
|
498
522
|
const NORMALIZED_ROUTE_PRIMITIVES = OXYGEN_PRIMITIVE_ROUTES.map((card) => card.primitive).sort();
|
|
@@ -518,15 +542,62 @@ export function inferCapabilityRoute(query) {
|
|
|
518
542
|
if (!normalized)
|
|
519
543
|
return null;
|
|
520
544
|
const explicit = explicitCapabilityIntent(normalized);
|
|
521
|
-
const
|
|
545
|
+
const catalogRow = findEnrichmentIntentByAlias(normalized);
|
|
546
|
+
// Card scoring stays the owner of routing; the catalogue only breaks a
|
|
547
|
+
// no-match tie ("get the funding round for these accounts" names no card
|
|
548
|
+
// term) and decorates the data cards it belongs to.
|
|
549
|
+
const card = explicit ?? highestScoringRoute(normalized) ?? (catalogRow ? cardForCatalogueIntent(catalogRow) : null);
|
|
522
550
|
if (!card)
|
|
523
551
|
return null;
|
|
524
552
|
const recommendations = recommendationsFor(card, normalized);
|
|
553
|
+
// The card's own recommendations are a pinned contract (tool order, no
|
|
554
|
+
// exact command on plain enrichment asks); the catalogue path travels
|
|
555
|
+
// beside them in `catalogIntent`, never inside them.
|
|
556
|
+
const catalogue = catalogRow && CATALOGUE_CARD_IDS.has(card.id) ? catalogueRecommendations(catalogRow, card) : null;
|
|
525
557
|
return {
|
|
526
558
|
card,
|
|
527
559
|
recommendedTools: recommendations.tools,
|
|
528
560
|
recommendedCommands: recommendations.commands,
|
|
529
561
|
...(recommendations.exactCommand ? { exactCommand: recommendations.exactCommand } : {}),
|
|
562
|
+
...(catalogue && catalogRow
|
|
563
|
+
? { catalogIntent: { id: catalogRow.id, label: catalogRow.label, status: catalogRow.status, group: catalogRow.group, tools: catalogue.tools, commands: catalogue.commands, exactCommand: catalogue.exactCommand } }
|
|
564
|
+
: {}),
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* A data-type ask ("tech stack", "decision makers at each account", "funding
|
|
569
|
+
* events") owns its card by taxonomy group: person/contact/company rows are
|
|
570
|
+
* Tables columns, decision-maker rows are the sourcing tools, signal rows are
|
|
571
|
+
* the signals card when one exists.
|
|
572
|
+
*/
|
|
573
|
+
function cardForCatalogueIntent(row) {
|
|
574
|
+
if (row.group === "decision_maker")
|
|
575
|
+
return ROUTE_BY_ID.get("sourcing-and-provider-tools") ?? null;
|
|
576
|
+
if (row.group === "signal")
|
|
577
|
+
return ROUTE_BY_ID.get("signals") ?? ROUTE_BY_PRIMITIVE.get("signals") ?? null;
|
|
578
|
+
return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
|
|
579
|
+
}
|
|
580
|
+
function catalogueRecommendations(row, card) {
|
|
581
|
+
const exactCommand = `oxygen enrichment catalog --intent ${row.id}`;
|
|
582
|
+
if (row.group === "decision_maker" || card.id === "sourcing-and-provider-tools") {
|
|
583
|
+
return {
|
|
584
|
+
tools: ["oxygen_enrichment_catalog", "oxygen_people_search_plan", "oxygen_people_search_run"],
|
|
585
|
+
commands: ["enrichment catalog", "people search plan", "people search run"],
|
|
586
|
+
exactCommand,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
if (row.group === "signal") {
|
|
590
|
+
return {
|
|
591
|
+
tools: ["oxygen_enrichment_catalog", "oxygen_signals_search_plan", "oxygen_signals_search_run"],
|
|
592
|
+
commands: ["enrichment catalog", "signals search plan", "signals search run"],
|
|
593
|
+
exactCommand,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
const find = row.surfaces.find ? [`oxygen_${row.surfaces.find.replace(/[\s-]+/g, "_")}`] : [];
|
|
597
|
+
return {
|
|
598
|
+
tools: ["oxygen_enrichment_catalog", "oxygen_columns_add", "oxygen_enrich_column_preview", ...find],
|
|
599
|
+
commands: ["enrichment catalog", "columns add", "enrich-column preview", ...(row.surfaces.find ? [row.surfaces.find] : [])],
|
|
600
|
+
exactCommand,
|
|
530
601
|
};
|
|
531
602
|
}
|
|
532
603
|
export function inferPrimitiveRoute(query) {
|
|
@@ -551,6 +622,9 @@ export function serializeCapabilityRoute(route) {
|
|
|
551
622
|
recommended_commands: route.recommendedCommands,
|
|
552
623
|
recommended_tools: route.recommendedTools,
|
|
553
624
|
...(route.exactCommand ? { exact_command: route.exactCommand } : {}),
|
|
625
|
+
...(route.catalogIntent
|
|
626
|
+
? { catalog_intent: { id: route.catalogIntent.id, label: route.catalogIntent.label, status: route.catalogIntent.status, group: route.catalogIntent.group, tools: route.catalogIntent.tools, commands: route.catalogIntent.commands, exact_command: route.catalogIntent.exactCommand } }
|
|
627
|
+
: {}),
|
|
554
628
|
hydrate: {
|
|
555
629
|
cli: "oxygen commands get <exact-command> --json",
|
|
556
630
|
mcp: "oxygen_capabilities_schema",
|
|
@@ -642,6 +716,10 @@ function explicitCapabilityIntent(query) {
|
|
|
642
716
|
// Monday verify new signups" still lands on Workflows.
|
|
643
717
|
if (isEmailVerificationIntent(query))
|
|
644
718
|
return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
|
|
719
|
+
// "verify these phone numbers" is a verification column on a table, not a
|
|
720
|
+
// dialer action; without this the sequences card's call terms win.
|
|
721
|
+
if (/\b(verify|validate|check)\b[^.]{0,40}\b(phone|mobile)\b/.test(query))
|
|
722
|
+
return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
|
|
645
723
|
if (/\b(recipe|playbook|proven play|what should i do)\b/.test(query)) {
|
|
646
724
|
return ROUTE_BY_PRIMITIVE.get("recipes") ?? null;
|
|
647
725
|
}
|
|
@@ -838,6 +916,16 @@ function recommendationsFor(card, query) {
|
|
|
838
916
|
commands: ["tables watcher preview", "tables watcher create", "tables watcher get", "tables watcher update", "tables watcher pause", "tables watcher resume"],
|
|
839
917
|
};
|
|
840
918
|
}
|
|
919
|
+
if (isCompanyResearchQuestionIntent(query)) {
|
|
920
|
+
// A fact the template catalog already answers by key: list the keys first
|
|
921
|
+
// (free), add the column by key (free), then price and run it. Ahead of
|
|
922
|
+
// the bare "run" noun below, because "which cloud provider does it run
|
|
923
|
+
// on" is a question about the company, not about a run.
|
|
924
|
+
return {
|
|
925
|
+
tools: ["oxygen_columns_catalog", "oxygen_columns_add", "oxygen_columns_run", "oxygen_tables_describe"],
|
|
926
|
+
commands: ["columns catalog", "columns add", "columns run", "tables describe"],
|
|
927
|
+
};
|
|
928
|
+
}
|
|
841
929
|
if (/\b(table )?(action )?runs?\b/.test(query)) {
|
|
842
930
|
return {
|
|
843
931
|
tools: ["oxygen_table_runs_get", "oxygen_table_runs_items", "oxygen_table_runs_wait", "oxygen_table_runs_retry_failed"],
|
|
@@ -1012,6 +1100,16 @@ function isEmailVerificationIntent(query) {
|
|
|
1012
1100
|
// ask ("find companies with a website in Germany") keeps its owner. LinkedIn
|
|
1013
1101
|
// page recovery is deliberately left to the LinkedIn rules above it, which
|
|
1014
1102
|
// already resolve company pages through the public-research catalog.
|
|
1103
|
+
/**
|
|
1104
|
+
* A company fact the template catalog answers by key — founders, parent company,
|
|
1105
|
+
* subsidiaries, cloud provider, NAICS, demos — and the "ready-made / template
|
|
1106
|
+
* catalog / research question" phrasing itself. Kept to nouns no other card
|
|
1107
|
+
* owns: "funding" alone belongs to Signals and "industry" alone to company
|
|
1108
|
+
* search, so both stay off this list.
|
|
1109
|
+
*/
|
|
1110
|
+
function isCompanyResearchQuestionIntent(query) {
|
|
1111
|
+
return /\b(founders?|founded by|parent compan(?:y|ies)|subsidiar(?:y|ies)|cloud provider|naics|column templates?|template catalog|ready[- ]made|research question|company research|offers? demos?|gives? demos?)\b/.test(query);
|
|
1112
|
+
}
|
|
1015
1113
|
function isCompanyUrlRecoveryIntent(query) {
|
|
1016
1114
|
const urlNoun = /\b(websites?|web ?sites?|website urls?|domains?|urls?|homepages?)\b/;
|
|
1017
1115
|
const companyNoun = /\b(compan(?:y|ies)|accounts?|organi[sz]ations?|brands?|vendors?)\b/;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Did I type this, or did Oxygen?" — the single rule behind the bolt.
|
|
3
|
+
*
|
|
4
|
+
* A CRM record mixes two kinds of field in one list: the handful a founder
|
|
5
|
+
* types (name, stage, owner, a phone they were given) and the majority Oxygen
|
|
6
|
+
* fills from one paid LinkedIn scrape and the free extractors over its payload.
|
|
7
|
+
* Reading a record without that distinction, every cell looks equally
|
|
8
|
+
* authoritative — which is exactly backwards, because a scraped headline and a
|
|
9
|
+
* hand-corrected job title deserve different trust.
|
|
10
|
+
*
|
|
11
|
+
* The grid already marks derived columns in its header (`ColumnDefinitionBadge`)
|
|
12
|
+
* and the record page did not, so the same field read as enriched in one place
|
|
13
|
+
* and hand-entered in the other. This is the shared answer both surfaces call.
|
|
14
|
+
*
|
|
15
|
+
* The rule is TOTAL over column kinds and deliberately coarse: a bolt means
|
|
16
|
+
* "you did not type this", and the label says what did. Splitting hairs between
|
|
17
|
+
* an AI column and a formula over an AI column's payload is an implementation
|
|
18
|
+
* detail a customer has no reason to hold.
|
|
19
|
+
*/
|
|
20
|
+
/** What produced the value in this column's cells. */
|
|
21
|
+
export type ColumnAutoFillKind =
|
|
22
|
+
/** A provider call — the paid lanes and everything extracted from their payloads. */
|
|
23
|
+
"enrichment"
|
|
24
|
+
/** A model wrote it (`ai`, `research`). */
|
|
25
|
+
| "ai"
|
|
26
|
+
/** Computed from other cells on the same row. */
|
|
27
|
+
| "derived"
|
|
28
|
+
/** Read across a link from another row or record. */
|
|
29
|
+
| "linked"
|
|
30
|
+
/** Oxygen's own bookkeeping — rollups, sync markers, engagement projections. */
|
|
31
|
+
| "system";
|
|
32
|
+
export type ColumnAutoFill = {
|
|
33
|
+
kind: ColumnAutoFillKind;
|
|
34
|
+
/** One short phrase, shown as the bolt's tooltip. Sentence case, no period. */
|
|
35
|
+
label: string;
|
|
36
|
+
};
|
|
37
|
+
/** The shape this rule needs. Structural so tenant-db, the web and the CLI all fit. */
|
|
38
|
+
export type AutoFillColumnInput = {
|
|
39
|
+
kind?: string | null;
|
|
40
|
+
definition?: Record<string, unknown> | null;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* What filled this column, or `null` when the answer is "a person did".
|
|
44
|
+
*
|
|
45
|
+
* `manual` and `source` columns are typed or imported by the workspace.
|
|
46
|
+
* `relation` and `bind` columns hold links a person or an identity match made,
|
|
47
|
+
* and the chip already says what it points at — a bolt there would mark the
|
|
48
|
+
* whole CRM as machine-written.
|
|
49
|
+
*/
|
|
50
|
+
export declare function columnAutoFill(column: AutoFillColumnInput): ColumnAutoFill | null;
|
|
51
|
+
/** True when the column's value comes from outside the workspace's own typing. */
|
|
52
|
+
export declare function isAutoFilledColumn(column: AutoFillColumnInput): boolean;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Did I type this, or did Oxygen?" — the single rule behind the bolt.
|
|
3
|
+
*
|
|
4
|
+
* A CRM record mixes two kinds of field in one list: the handful a founder
|
|
5
|
+
* types (name, stage, owner, a phone they were given) and the majority Oxygen
|
|
6
|
+
* fills from one paid LinkedIn scrape and the free extractors over its payload.
|
|
7
|
+
* Reading a record without that distinction, every cell looks equally
|
|
8
|
+
* authoritative — which is exactly backwards, because a scraped headline and a
|
|
9
|
+
* hand-corrected job title deserve different trust.
|
|
10
|
+
*
|
|
11
|
+
* The grid already marks derived columns in its header (`ColumnDefinitionBadge`)
|
|
12
|
+
* and the record page did not, so the same field read as enriched in one place
|
|
13
|
+
* and hand-entered in the other. This is the shared answer both surfaces call.
|
|
14
|
+
*
|
|
15
|
+
* The rule is TOTAL over column kinds and deliberately coarse: a bolt means
|
|
16
|
+
* "you did not type this", and the label says what did. Splitting hairs between
|
|
17
|
+
* an AI column and a formula over an AI column's payload is an implementation
|
|
18
|
+
* detail a customer has no reason to hold.
|
|
19
|
+
*/
|
|
20
|
+
function readString(definition, key) {
|
|
21
|
+
const value = definition?.[key];
|
|
22
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A `fill: "empty"` formula is not an ordinary calculation. It is the codebase's
|
|
26
|
+
* "an extracted fact is a FIELD, not a formula" shape: the expression runs once
|
|
27
|
+
* over an enrichment payload the row already paid for, stores a real typed
|
|
28
|
+
* value, and leaves a human's correction alone on the next run. To the person
|
|
29
|
+
* reading the record that is enrichment, not arithmetic.
|
|
30
|
+
*/
|
|
31
|
+
function isExtractedFromPayload(column) {
|
|
32
|
+
return readString(column.definition, "fill") === "empty";
|
|
33
|
+
}
|
|
34
|
+
/** Name the provider lane when the column declares one, so the tooltip is specific. */
|
|
35
|
+
function enrichmentLabel(column) {
|
|
36
|
+
const toolId = readString(column.definition, "toolId") ?? readString(column.definition, "tool_id");
|
|
37
|
+
// Vendor names never reach a customer surface — the managed scraper is a
|
|
38
|
+
// deliberately vendor-neutral rail (`packages/providers/src/descriptors/scraper.ts`),
|
|
39
|
+
// and tests fail if the vendor behind it leaks. Speak in terms of the SOURCE.
|
|
40
|
+
if (toolId?.startsWith("scraper.linkedin"))
|
|
41
|
+
return "Enriched from LinkedIn";
|
|
42
|
+
if (toolId)
|
|
43
|
+
return "Enriched by a data provider";
|
|
44
|
+
return "Enriched automatically";
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* What filled this column, or `null` when the answer is "a person did".
|
|
48
|
+
*
|
|
49
|
+
* `manual` and `source` columns are typed or imported by the workspace.
|
|
50
|
+
* `relation` and `bind` columns hold links a person or an identity match made,
|
|
51
|
+
* and the chip already says what it points at — a bolt there would mark the
|
|
52
|
+
* whole CRM as machine-written.
|
|
53
|
+
*/
|
|
54
|
+
export function columnAutoFill(column) {
|
|
55
|
+
switch (column.kind) {
|
|
56
|
+
case "tool":
|
|
57
|
+
case "enrichment":
|
|
58
|
+
return { kind: "enrichment", label: enrichmentLabel(column) };
|
|
59
|
+
case "ai":
|
|
60
|
+
return { kind: "ai", label: "Written by an AI column" };
|
|
61
|
+
case "research":
|
|
62
|
+
return { kind: "ai", label: "Researched on the web by AI" };
|
|
63
|
+
case "formula":
|
|
64
|
+
return isExtractedFromPayload(column)
|
|
65
|
+
? { kind: "enrichment", label: "Extracted from enrichment data" }
|
|
66
|
+
: { kind: "derived", label: "Calculated from other fields" };
|
|
67
|
+
case "lookup":
|
|
68
|
+
return { kind: "linked", label: "Read from a linked record" };
|
|
69
|
+
case "workflow":
|
|
70
|
+
return { kind: "system", label: "Written by a workflow" };
|
|
71
|
+
case "system":
|
|
72
|
+
return { kind: "system", label: "Maintained by Oxygen" };
|
|
73
|
+
default:
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** True when the column's value comes from outside the workspace's own typing. */
|
|
78
|
+
export function isAutoFilledColumn(column) {
|
|
79
|
+
return columnAutoFill(column) !== null;
|
|
80
|
+
}
|
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
* may contain a dot or a space. Such a field carries `referenceable: false` and
|
|
27
27
|
* is display-only.
|
|
28
28
|
*/
|
|
29
|
-
import { buildResearchCellSchema, deriveResearchOutputContract, usesServerManagedResearchSchema, } from "./research-output-contract.js";
|
|
29
|
+
import { buildResearchCellSchema, isResearchEnvelopeSchema, researchEvidenceRequired, deriveResearchOutputContract, usesServerManagedResearchSchema, } from "./research-output-contract.js";
|
|
30
30
|
import { isTemplateSafePath, parseJsonPath, readJsonPath } from "./json-path.js";
|
|
31
|
+
import { isRecord } from "./type-guards.js";
|
|
32
|
+
import { readString } from "./value-readers.js";
|
|
31
33
|
const MAX_FIELDS = 40;
|
|
32
34
|
const MAX_SCHEMA_DEPTH = 5;
|
|
33
35
|
const EMPTY_CONTRACT = {
|
|
@@ -36,12 +38,6 @@ const EMPTY_CONTRACT = {
|
|
|
36
38
|
primaryField: null,
|
|
37
39
|
serverManaged: false,
|
|
38
40
|
};
|
|
39
|
-
function isRecord(value) {
|
|
40
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
|
-
}
|
|
42
|
-
function readString(value) {
|
|
43
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
44
|
-
}
|
|
45
41
|
export function humanizeFieldSegment(segment) {
|
|
46
42
|
const titled = segment
|
|
47
43
|
.split(/[_\s-]+/)
|
|
@@ -263,13 +259,21 @@ function researchContract(definition) {
|
|
|
263
259
|
const prompt = readString(definition.prompt) ?? "";
|
|
264
260
|
const webSearch = isRecord(definition.webSearch) ? definition.webSearch : null;
|
|
265
261
|
const contract = deriveResearchOutputContract(prompt, {
|
|
266
|
-
evidenceRequired: webSearch
|
|
262
|
+
evidenceRequired: researchEvidenceRequired(webSearch),
|
|
267
263
|
});
|
|
268
264
|
// The STORED shape, not the model-facing one: a research cell carries
|
|
269
|
-
// `sources`, never the `citations` the model answered with.
|
|
265
|
+
// `sources`, never the `citations` the model answered with. A column whose
|
|
266
|
+
// own schema is still the research envelope (a catalog template's typed
|
|
267
|
+
// answer) gets the same rewrite, so `answer.plan_count` is offered and
|
|
268
|
+
// `citations` is not; a hand-authored schema that replaced the envelope is
|
|
269
|
+
// used verbatim, because the cell then holds exactly what it declares.
|
|
270
270
|
const schema = serverManaged
|
|
271
271
|
? buildResearchCellSchema(contract)
|
|
272
|
-
:
|
|
272
|
+
: isRecord(definition.outputSchema)
|
|
273
|
+
? (isResearchEnvelopeSchema(definition.outputSchema)
|
|
274
|
+
? buildResearchCellSchema(null, definition.outputSchema)
|
|
275
|
+
: definition.outputSchema)
|
|
276
|
+
: null;
|
|
273
277
|
if (!schema)
|
|
274
278
|
return EMPTY_CONTRACT;
|
|
275
279
|
return {
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export declare const COMPANY_ENRICHMENT_FIELD_CATEGORIES: readonly ["identity", "firmographics", "funding", "relationships", "signals", "technology", "web_presence"];
|
|
2
|
+
export type CompanyEnrichmentFieldCategory = (typeof COMPANY_ENRICHMENT_FIELD_CATEGORIES)[number];
|
|
3
|
+
export declare const COMPANY_ENRICHMENT_FIELD_CATEGORY_LABELS: Record<CompanyEnrichmentFieldCategory, string>;
|
|
4
|
+
export type CompanyEnrichmentFieldDataType = "text" | "numeric" | "jsonb";
|
|
5
|
+
/**
|
|
6
|
+
* Where a DERIVED field reads its value from: another field's already-resolved
|
|
7
|
+
* payload. `autoRun: true` means requesting the derived field on its own fetches
|
|
8
|
+
* that source first (the source's own lanes run as a pre-step, billed and
|
|
9
|
+
* disclosed in the plan). `autoRun: false` means the source is consulted only
|
|
10
|
+
* when the same run resolved it anyway or the row already holds it — never
|
|
11
|
+
* fetched just for this field, because it would cost more than the field is
|
|
12
|
+
* worth (a 490-credit funding lookup for a 5-credit Facebook URL).
|
|
13
|
+
*/
|
|
14
|
+
export type CompanyEnrichmentDerivedSource = {
|
|
15
|
+
field: CompanyEnrichmentFieldKey;
|
|
16
|
+
autoRun: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type CompanyEnrichmentFieldDefinition = {
|
|
19
|
+
field: CompanyEnrichmentFieldKey;
|
|
20
|
+
label: string;
|
|
21
|
+
category: CompanyEnrichmentFieldCategory;
|
|
22
|
+
/** ONE founder-voice line, rendered next to the field in every picker. */
|
|
23
|
+
description: string;
|
|
24
|
+
/** Default target column key when a table has no matching column. */
|
|
25
|
+
columnKey: string;
|
|
26
|
+
dataType: CompanyEnrichmentFieldDataType;
|
|
27
|
+
/** Semantic type stamped on the created target column, when one applies. */
|
|
28
|
+
semanticType: string | null;
|
|
29
|
+
/** Ordered sources a derived field reads; empty for a lane-resolved field. */
|
|
30
|
+
derivedFrom: readonly CompanyEnrichmentDerivedSource[];
|
|
31
|
+
/** Extra caller input the field needs before it can answer. */
|
|
32
|
+
requires?: "check_technologies";
|
|
33
|
+
/**
|
|
34
|
+
* `coming_soon` (D51, 2026-09-19): the field keeps its vocabulary entry and
|
|
35
|
+
* column key so existing columns still render, but it has no provider lane,
|
|
36
|
+
* no picker group and no `--fields` acceptance until its lane is re-measured.
|
|
37
|
+
*/
|
|
38
|
+
availability?: "coming_soon";
|
|
39
|
+
};
|
|
40
|
+
export type CompanyEnrichmentFieldKey = "domain" | "linkedin_url" | "headcount" | "industry" | "funding" | "revenue" | "technologies" | "hiring_signals" | "company_profile" | "description" | "founded_year" | "hq_address" | "hq_country" | "logo_url" | "specialties" | "company_type" | "funding_stage" | "latest_funding_round" | "total_funding" | "funding_rounds_count" | "investors" | "competitors" | "corporate_structure" | "parent_company" | "subsidiaries" | "acquisitions" | "news" | "job_openings" | "technology_check" | "crunchbase_url" | "github_url" | "facebook_url" | "instagram_url" | "youtube_url";
|
|
41
|
+
export declare const COMPANY_ENRICHMENT_FIELD_DEFINITIONS: readonly CompanyEnrichmentFieldDefinition[];
|
|
42
|
+
export declare const COMPANY_ENRICHMENT_FIELD_KEYS: readonly CompanyEnrichmentFieldKey[];
|
|
43
|
+
export declare function isCompanyEnrichmentFieldKey(value: string): value is CompanyEnrichmentFieldKey;
|
|
44
|
+
export declare function companyEnrichmentFieldDefinition(field: CompanyEnrichmentFieldKey): CompanyEnrichmentFieldDefinition;
|
|
45
|
+
/** D51: a field parked `coming_soon` has no lane, no picker group and is refused by `--fields`. */
|
|
46
|
+
export declare function isComingSoonCompanyEnrichmentField(field: CompanyEnrichmentFieldKey): boolean;
|
|
47
|
+
/** A field whose value is read out of another field's payload, never a provider call of its own. */
|
|
48
|
+
export declare function isDerivedCompanyEnrichmentField(field: CompanyEnrichmentFieldKey): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* What `find company` resolves when the caller names no fields. Identity plus
|
|
51
|
+
* the firmographics that cost nothing beyond the one profile lookup the
|
|
52
|
+
* headcount/industry lanes already make; funding, relationship and web-presence
|
|
53
|
+
* fields stay opt-in because each adds a priced lane.
|
|
54
|
+
*/
|
|
55
|
+
export declare const DEFAULT_COMPANY_FIND_FIELDS: readonly CompanyEnrichmentFieldKey[];
|
|
56
|
+
/**
|
|
57
|
+
* What `columns add --preset company_enrich` creates when the caller names no
|
|
58
|
+
* fields: the identity/firmographic set plus the full profile and the funding
|
|
59
|
+
* facts LinkedIn publishes for free off that same profile. Every lane-priced
|
|
60
|
+
* relationship, signal, technology and web-presence field is opt-in through
|
|
61
|
+
* `--fields`, so the default bundle's per-row ceiling stays the profile chain's.
|
|
62
|
+
*/
|
|
63
|
+
export declare const DEFAULT_COMPANY_PRESET_FIELDS: readonly CompanyEnrichmentFieldKey[];
|
|
64
|
+
export type CompanyEnrichmentFieldGroupId = "profile_details" | "funding_details" | "job_openings" | "tech_stack" | "technology_check";
|
|
65
|
+
export type CompanyEnrichmentFieldGroup = {
|
|
66
|
+
id: CompanyEnrichmentFieldGroupId;
|
|
67
|
+
/** Picker row and catalog label, in customer words. */
|
|
68
|
+
label: string;
|
|
69
|
+
/** ONE founder-voice line. No provider names. */
|
|
70
|
+
description: string;
|
|
71
|
+
/** The catalog fields this group adds to the company bundle, in column order. */
|
|
72
|
+
fields: readonly CompanyEnrichmentFieldKey[];
|
|
73
|
+
/**
|
|
74
|
+
* What the group does to the per-row bill.
|
|
75
|
+
*
|
|
76
|
+
* `included` — every field is read out of the company profile the default
|
|
77
|
+
* bundle already fetches, so ticking it adds columns and no credits.
|
|
78
|
+
* `extra` — at least one field has its own provider lookup, so the run can
|
|
79
|
+
* bill more per row. The exact number depends on which lane answers, which
|
|
80
|
+
* only a dry run over the real rows can say; no surface quotes one here.
|
|
81
|
+
*/
|
|
82
|
+
cost: "included" | "extra";
|
|
83
|
+
/** Set when the group cannot be added without extra caller input. */
|
|
84
|
+
requires?: "check_technologies";
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* The opt-in company fields, grouped the way a customer asks for them ("add
|
|
88
|
+
* competitors", "add their tech stack"). Every surface that offers a company
|
|
89
|
+
* field beyond the default bundle reads THIS list: the enrichment column catalog
|
|
90
|
+
* (`oxygen enrichment catalog`, `oxygen_enrichment_catalog`), the web add-column
|
|
91
|
+
* picker, and the catalog pricing. Adding a group adds its fields to the SAME
|
|
92
|
+
* company enrichment column — re-applying the bundle widens the existing
|
|
93
|
+
* column's field list instead of creating a second paid column.
|
|
94
|
+
*/
|
|
95
|
+
export declare const COMPANY_ENRICHMENT_FIELD_GROUPS: readonly CompanyEnrichmentFieldGroup[];
|
|
96
|
+
export declare function companyEnrichmentFieldGroup(id: string): CompanyEnrichmentFieldGroup | null;
|
|
97
|
+
/**
|
|
98
|
+
* Parse a comma-separated or array field list against the catalog. Returns the
|
|
99
|
+
* unique, lowercased keys in request order, or throws naming every unknown
|
|
100
|
+
* entry with the supported list — never a silent fallback to a default set,
|
|
101
|
+
* which is how `--fields tech,hiring,profile` once ran the four default fields
|
|
102
|
+
* and billed for an answer nobody asked for (OXP-7.2.9).
|
|
103
|
+
*/
|
|
104
|
+
export declare function parseCompanyEnrichmentFieldList(value: unknown): {
|
|
105
|
+
fields: CompanyEnrichmentFieldKey[];
|
|
106
|
+
unknown: string[];
|
|
107
|
+
coming_soon: CompanyEnrichmentFieldKey[];
|
|
108
|
+
};
|