@oxygen-agent/cli 1.987.20 → 1.1010.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/admin-primary-providers-render.d.ts +0 -2
- package/dist/admin-primary-providers-render.js +1 -1
- package/dist/browser-login.js +1 -4
- package/dist/column-decision-options.d.ts +20 -0
- package/dist/column-decision-options.js +54 -0
- package/dist/command-manifest.d.ts +3 -2
- package/dist/command-manifest.js +25 -2
- package/dist/credentials.d.ts +1 -1
- package/dist/functions-commands.js +13 -9
- package/dist/help.d.ts +8 -0
- package/dist/help.js +46 -0
- package/dist/index.js +2751 -242
- package/dist/knowledge-mirror.d.ts +2 -2
- package/dist/runtime.d.ts +0 -15
- package/dist/runtime.js +1 -1
- package/dist/search-ai-filter-notice.d.ts +17 -0
- package/dist/search-ai-filter-notice.js +38 -0
- package/dist/session.d.ts +4 -3
- package/dist/skills.d.ts +8 -7
- package/dist/skills.js +58 -20
- package/dist/transcript.d.ts +2 -1
- package/dist/util.d.ts +10 -1
- package/dist/util.js +14 -2
- package/node_modules/@oxygen/cli-ugc/dist/commands.js +296 -140
- package/node_modules/@oxygen/cli-ugc/dist/field-parser.d.ts +9 -0
- package/node_modules/@oxygen/cli-ugc/dist/field-parser.js +34 -0
- package/node_modules/@oxygen/formula/dist/coerce.d.ts +10 -0
- package/node_modules/@oxygen/formula/dist/coerce.js +10 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.js +65 -0
- package/node_modules/@oxygen/formula/dist/hash.d.ts +19 -0
- package/node_modules/@oxygen/formula/dist/hash.js +199 -0
- package/node_modules/@oxygen/formula/dist/value-cleaners.d.ts +6 -1
- package/node_modules/@oxygen/formula/dist/value-cleaners.js +10 -26
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +13 -0
- package/node_modules/@oxygen/shared/dist/array-utils.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/array-utils.js +11 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +99 -22
- package/node_modules/@oxygen/shared/dist/billing.js +195 -40
- package/node_modules/@oxygen/shared/dist/byok-connect.js +5 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +27 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +311 -28
- package/node_modules/@oxygen/shared/dist/cli-http-error.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/cli-http-error.js +8 -0
- package/node_modules/@oxygen/shared/dist/cli-result.js +1 -0
- package/node_modules/@oxygen/shared/dist/column-autofill.d.ts +52 -0
- package/node_modules/@oxygen/shared/dist/column-autofill.js +62 -0
- package/node_modules/@oxygen/shared/dist/column-decision.d.ts +50 -0
- package/node_modules/@oxygen/shared/dist/column-decision.js +228 -0
- package/node_modules/@oxygen/shared/dist/column-output-fields.js +2 -6
- package/node_modules/@oxygen/shared/dist/company-enrichment-fields.d.ts +113 -0
- package/node_modules/@oxygen/shared/dist/company-enrichment-fields.js +548 -0
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +6 -6
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +6 -6
- package/node_modules/@oxygen/shared/dist/cutover-freeze.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/cutover-freeze.js +52 -0
- package/node_modules/@oxygen/shared/dist/data-suppliers.d.ts +57 -0
- package/node_modules/@oxygen/shared/dist/data-suppliers.js +59 -0
- package/node_modules/@oxygen/shared/dist/deploy-env.d.ts +74 -0
- package/node_modules/@oxygen/shared/dist/deploy-env.js +82 -0
- package/node_modules/@oxygen/shared/dist/dnc-rules.d.ts +130 -0
- package/node_modules/@oxygen/shared/dist/dnc-rules.js +221 -0
- package/node_modules/@oxygen/shared/dist/enrichment-intents.d.ts +107 -0
- package/node_modules/@oxygen/shared/dist/enrichment-intents.js +809 -0
- package/node_modules/@oxygen/shared/dist/error-message.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/error-message.js +3 -0
- package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -3
- package/node_modules/@oxygen/shared/dist/external-write-policy.d.ts +33 -0
- package/node_modules/@oxygen/shared/dist/external-write-policy.js +68 -0
- package/node_modules/@oxygen/shared/dist/format-percent.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/format-percent.js +13 -0
- package/node_modules/@oxygen/shared/dist/freemail-domains.d.ts +81 -0
- package/node_modules/@oxygen/shared/dist/freemail-domains.js +157 -0
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +1 -1
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +60 -4
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +125 -10
- package/node_modules/@oxygen/shared/dist/index.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/index.js +15 -0
- package/node_modules/@oxygen/shared/dist/json-path.js +1 -3
- package/node_modules/@oxygen/shared/dist/knowledge-bases.js +1 -3
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +2 -2
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +2 -2
- package/node_modules/@oxygen/shared/dist/langfuse.d.ts +44 -1
- package/node_modules/@oxygen/shared/dist/langfuse.js +416 -14
- package/node_modules/@oxygen/shared/dist/linkedin-countries.d.ts +33 -0
- package/node_modules/@oxygen/shared/dist/linkedin-countries.js +361 -0
- package/node_modules/@oxygen/shared/dist/linkedin-country-timezones.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/linkedin-country-timezones.js +276 -0
- package/node_modules/@oxygen/shared/dist/linkedin-message-deletion.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/linkedin-message-deletion.js +5 -0
- package/node_modules/@oxygen/shared/dist/linkedin-post-keywords.d.ts +44 -0
- package/node_modules/@oxygen/shared/dist/linkedin-post-keywords.js +116 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +96 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +123 -0
- package/node_modules/@oxygen/shared/dist/llm-durable-capture.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/llm-durable-capture.js +89 -0
- package/node_modules/@oxygen/shared/dist/llm-prompts.d.ts +75 -0
- package/node_modules/@oxygen/shared/dist/llm-prompts.js +161 -0
- package/node_modules/@oxygen/shared/dist/log-sink-selector.d.ts +39 -0
- package/node_modules/@oxygen/shared/dist/log-sink-selector.js +56 -0
- package/node_modules/@oxygen/shared/dist/log.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/log.js +6 -1
- package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.d.ts +90 -0
- package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.js +130 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +21 -0
- package/node_modules/@oxygen/shared/dist/operational-telemetry.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/operational-telemetry.js +73 -0
- package/node_modules/@oxygen/shared/dist/otlp-log-sink.d.ts +79 -0
- package/node_modules/@oxygen/shared/dist/otlp-log-sink.js +366 -0
- package/node_modules/@oxygen/shared/dist/plan-capabilities.js +1 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +23 -22
- package/node_modules/@oxygen/shared/dist/plan-limits.js +45 -18
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +48 -41
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +36 -25
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +22 -22
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +40 -34
- package/node_modules/@oxygen/shared/dist/product-analytics-environment.js +9 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +36 -2
- package/node_modules/@oxygen/shared/dist/product-analytics-events.js +36 -1
- package/node_modules/@oxygen/shared/dist/rate-window.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/rate-window.js +8 -0
- package/node_modules/@oxygen/shared/dist/redaction.js +4 -1
- package/node_modules/@oxygen/shared/dist/research-output-contract.js +1 -3
- package/node_modules/@oxygen/shared/dist/scraper-lane-credential.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/scraper-lane-credential.js +23 -0
- package/node_modules/@oxygen/shared/dist/search-vocab.js +4 -5
- package/node_modules/@oxygen/shared/dist/select-options.js +6 -1
- package/node_modules/@oxygen/shared/dist/sequence-failures.js +1 -5
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +23 -0
- package/node_modules/@oxygen/shared/dist/sequences.js +115 -5
- package/node_modules/@oxygen/shared/dist/signup-lead-payload.d.ts +80 -0
- package/node_modules/@oxygen/shared/dist/signup-lead-payload.js +198 -0
- package/node_modules/@oxygen/shared/dist/social-capabilities.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/social-capabilities.js +25 -16
- package/node_modules/@oxygen/shared/dist/social-post-metrics-core.d.ts +32 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-core.js +32 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-linkedin.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-linkedin.js +103 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-series.d.ts +96 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-series.js +213 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-x.d.ts +13 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics-x.js +78 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics.d.ts +36 -0
- package/node_modules/@oxygen/shared/dist/social-post-metrics.js +51 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +22 -10
- package/node_modules/@oxygen/shared/dist/spend-safety.js +15 -21
- package/node_modules/@oxygen/shared/dist/sql-rows.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/sql-rows.js +3 -0
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +36 -0
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +184 -0
- package/node_modules/@oxygen/shared/dist/stripe-subscription-kind.d.ts +41 -0
- package/node_modules/@oxygen/shared/dist/stripe-subscription-kind.js +44 -0
- package/node_modules/@oxygen/shared/dist/table-limits.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/table-limits.js +3 -0
- package/node_modules/@oxygen/shared/dist/telemetry-export-observer.d.ts +94 -0
- package/node_modules/@oxygen/shared/dist/telemetry-export-observer.js +298 -0
- package/node_modules/@oxygen/shared/dist/telemetry.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/telemetry.js +28 -2
- package/node_modules/@oxygen/shared/dist/type-guards.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/type-guards.js +35 -0
- package/node_modules/@oxygen/shared/dist/ugc.d.ts +22 -11
- package/node_modules/@oxygen/shared/dist/ugc.js +10 -0
- package/node_modules/@oxygen/shared/dist/value-readers.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/value-readers.js +59 -0
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/version.generated.js +2 -0
- package/node_modules/@oxygen/shared/dist/version.js +8 -1
- package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +0 -23
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +29 -0
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +31 -0
- package/node_modules/@oxygen/shared/package.json +59 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +2 -5
- package/node_modules/@oxygen/workflows/dist/graph/params.js +1 -1
- package/package.json +3 -2
|
@@ -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,62 @@
|
|
|
1
|
+
import { PROFESSIONAL_NETWORK_DATA_LABEL } from "./data-suppliers.js";
|
|
2
|
+
function readString(definition, key) {
|
|
3
|
+
const value = definition?.[key];
|
|
4
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A `fill: "empty"` formula is not an ordinary calculation. It is the codebase's
|
|
8
|
+
* "an extracted fact is a FIELD, not a formula" shape: the expression runs once
|
|
9
|
+
* over an enrichment payload the row already paid for, stores a real typed
|
|
10
|
+
* value, and leaves a human's correction alone on the next run. To the person
|
|
11
|
+
* reading the record that is enrichment, not arithmetic.
|
|
12
|
+
*/
|
|
13
|
+
function isExtractedFromPayload(column) {
|
|
14
|
+
return readString(column.definition, "fill") === "empty";
|
|
15
|
+
}
|
|
16
|
+
/** Name the provider lane when the column declares one, so the tooltip is specific. */
|
|
17
|
+
function enrichmentLabel(column) {
|
|
18
|
+
const toolId = readString(column.definition, "toolId") ?? readString(column.definition, "tool_id");
|
|
19
|
+
// Name the source and its supplier (Philipp, 2026-09-25: suppliers are named;
|
|
20
|
+
// data-extraction labels say "Professional Network"). The label comes from
|
|
21
|
+
// one owner so it cannot drift from the picker and the Connections card.
|
|
22
|
+
if (toolId?.startsWith("scraper.linkedin"))
|
|
23
|
+
return `Enriched from ${PROFESSIONAL_NETWORK_DATA_LABEL}`;
|
|
24
|
+
if (toolId)
|
|
25
|
+
return "Enriched by a data provider";
|
|
26
|
+
return "Enriched automatically";
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* What filled this column, or `null` when the answer is "a person did".
|
|
30
|
+
*
|
|
31
|
+
* `manual` and `source` columns are typed or imported by the workspace.
|
|
32
|
+
* `relation` and `bind` columns hold links a person or an identity match made,
|
|
33
|
+
* and the chip already says what it points at — a bolt there would mark the
|
|
34
|
+
* whole CRM as machine-written.
|
|
35
|
+
*/
|
|
36
|
+
export function columnAutoFill(column) {
|
|
37
|
+
switch (column.kind) {
|
|
38
|
+
case "tool":
|
|
39
|
+
case "enrichment":
|
|
40
|
+
return { kind: "enrichment", label: enrichmentLabel(column) };
|
|
41
|
+
case "ai":
|
|
42
|
+
return { kind: "ai", label: "Written by an AI column" };
|
|
43
|
+
case "research":
|
|
44
|
+
return { kind: "ai", label: "Researched on the web by AI" };
|
|
45
|
+
case "formula":
|
|
46
|
+
return isExtractedFromPayload(column)
|
|
47
|
+
? { kind: "enrichment", label: "Extracted from enrichment data" }
|
|
48
|
+
: { kind: "derived", label: "Calculated from other fields" };
|
|
49
|
+
case "lookup":
|
|
50
|
+
return { kind: "linked", label: "Read from a linked record" };
|
|
51
|
+
case "workflow":
|
|
52
|
+
return { kind: "system", label: "Written by a workflow" };
|
|
53
|
+
case "system":
|
|
54
|
+
return { kind: "system", label: "Maintained by Oxygen" };
|
|
55
|
+
default:
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** True when the column's value comes from outside the workspace's own typing. */
|
|
60
|
+
export function isAutoFilledColumn(column) {
|
|
61
|
+
return columnAutoFill(column) !== null;
|
|
62
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export declare const COLUMN_DECISION_ENGINE = "system_one";
|
|
2
|
+
export declare const COLUMN_DECISION_MODES: readonly ["labels", "yes_no", "score"];
|
|
3
|
+
export type ColumnDecisionMode = (typeof COLUMN_DECISION_MODES)[number];
|
|
4
|
+
export declare const COLUMN_DECISION_GROUNDINGS: readonly ["workspace", "none"];
|
|
5
|
+
export type ColumnDecisionGrounding = (typeof COLUMN_DECISION_GROUNDINGS)[number];
|
|
6
|
+
export declare const COLUMN_DECISION_MIN_LABELS = 2;
|
|
7
|
+
export declare const COLUMN_DECISION_MAX_LABELS = 20;
|
|
8
|
+
export declare const COLUMN_DECISION_MIN_LEVELS = 2;
|
|
9
|
+
export declare const COLUMN_DECISION_MAX_LEVELS = 10;
|
|
10
|
+
export declare const COLUMN_DECISION_MAX_OPTION_CHARS = 80;
|
|
11
|
+
/** Floors the ADR shipped with: 0.85 for a choice or score, a 70/30 band for yes/no. */
|
|
12
|
+
export declare const COLUMN_DECISION_DEFAULT_MIN_CONFIDENCE: Readonly<Record<ColumnDecisionMode, number>>;
|
|
13
|
+
export declare const COLUMN_DECISION_DEFAULT_FIELD: Readonly<Record<ColumnDecisionMode, string>>;
|
|
14
|
+
/** Keys the cell writes next to the answer field; a field may not reuse one. */
|
|
15
|
+
export declare const COLUMN_DECISION_RESERVED_FIELDS: ReadonlySet<string>;
|
|
16
|
+
export type ColumnDecisionDefinition = {
|
|
17
|
+
engine: typeof COLUMN_DECISION_ENGINE;
|
|
18
|
+
mode: ColumnDecisionMode;
|
|
19
|
+
/** The cell key the answer lands under. */
|
|
20
|
+
field: string;
|
|
21
|
+
/** `labels` mode: the options, in the customer's own words. */
|
|
22
|
+
labels?: string[];
|
|
23
|
+
/** `score` mode: ordered levels, lowest first. */
|
|
24
|
+
levels?: string[];
|
|
25
|
+
/** Below this the answer is kept as a best guess marked uncertain. */
|
|
26
|
+
minConfidence: number;
|
|
27
|
+
/** `workspace` sends the column's workspace knowledge slice in the state; `none` (the default) judges the row alone. */
|
|
28
|
+
grounding: ColumnDecisionGrounding;
|
|
29
|
+
};
|
|
30
|
+
/** A validation failure naming the field, for the normalizer to wrap in its own error type. */
|
|
31
|
+
export declare class ColumnDecisionError extends Error {
|
|
32
|
+
readonly field: string;
|
|
33
|
+
readonly value: unknown;
|
|
34
|
+
constructor(message: string, field: string, value?: unknown);
|
|
35
|
+
}
|
|
36
|
+
/** True for any definition that carries a `decision` object, valid or not. */
|
|
37
|
+
export declare function hasColumnDecision(definition: unknown): boolean;
|
|
38
|
+
/** The stored decision, or null. Assumes the normalizer already validated it. */
|
|
39
|
+
export declare function readColumnDecision(definition: unknown): ColumnDecisionDefinition | null;
|
|
40
|
+
/**
|
|
41
|
+
* Validate and fill a caller-supplied decision. Throws {@link ColumnDecisionError}
|
|
42
|
+
* naming the bad field; the caller maps it to its own error envelope.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeColumnDecision(value: unknown): ColumnDecisionDefinition;
|
|
45
|
+
/**
|
|
46
|
+
* The cell's JSON Schema, derived from the decision so filters, sorts, sub-columns
|
|
47
|
+
* and `{{column.field}}` references see a typed answer: an enum for labels, a
|
|
48
|
+
* boolean for yes/no, a number for a score.
|
|
49
|
+
*/
|
|
50
|
+
export declare function columnDecisionOutputSchema(decision: ColumnDecisionDefinition): Record<string, unknown>;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The classification column contract: an AI column whose `definition.decision`
|
|
3
|
+
* asks System One (TypeSafe Jev, ADR 0027) a closed question per row instead of
|
|
4
|
+
* asking a generative model for prose.
|
|
5
|
+
*
|
|
6
|
+
* Three modes, one per System One question type:
|
|
7
|
+
*
|
|
8
|
+
* - `labels` — pick one of 2–20 customer labels (a `choice`).
|
|
9
|
+
* - `yes_no` — a yes/no probability (a `noul`). Its confidence is how far the
|
|
10
|
+
* probability sits from 50%, so a 0.7 floor means "yes at 70% or more, no at
|
|
11
|
+
* 30% or less".
|
|
12
|
+
* - `score` — rate against 2–10 ordered levels, lowest first (a `score`). The
|
|
13
|
+
* value is the vendor's 0-based position, so it sorts and filters as a number.
|
|
14
|
+
*
|
|
15
|
+
* The column's own `prompt` is the question. Row values never enter it: the
|
|
16
|
+
* runner rewrites `{{tokens}}` to bracketed names and sends the values in the
|
|
17
|
+
* state (ADR 0027 §5). Below the floor the cell keeps the best guess, marked
|
|
18
|
+
* `uncertain` with its confidence; there is no generative fallback on this
|
|
19
|
+
* surface (ADR 0027 amendment 2026-09-25).
|
|
20
|
+
*
|
|
21
|
+
* Pure and client-safe: the tenant-db normalizer, the runner, the web editor,
|
|
22
|
+
* the CLI and the MCP server all read the same shape from here.
|
|
23
|
+
*/
|
|
24
|
+
import { isRecord } from "./type-guards.js";
|
|
25
|
+
export const COLUMN_DECISION_ENGINE = "system_one";
|
|
26
|
+
export const COLUMN_DECISION_MODES = ["labels", "yes_no", "score"];
|
|
27
|
+
export const COLUMN_DECISION_GROUNDINGS = ["workspace", "none"];
|
|
28
|
+
export const COLUMN_DECISION_MIN_LABELS = 2;
|
|
29
|
+
export const COLUMN_DECISION_MAX_LABELS = 20;
|
|
30
|
+
export const COLUMN_DECISION_MIN_LEVELS = 2;
|
|
31
|
+
export const COLUMN_DECISION_MAX_LEVELS = 10;
|
|
32
|
+
export const COLUMN_DECISION_MAX_OPTION_CHARS = 80;
|
|
33
|
+
/** Floors the ADR shipped with: 0.85 for a choice or score, a 70/30 band for yes/no. */
|
|
34
|
+
export const COLUMN_DECISION_DEFAULT_MIN_CONFIDENCE = {
|
|
35
|
+
labels: 0.85,
|
|
36
|
+
yes_no: 0.7,
|
|
37
|
+
score: 0.85,
|
|
38
|
+
};
|
|
39
|
+
export const COLUMN_DECISION_DEFAULT_FIELD = {
|
|
40
|
+
labels: "label",
|
|
41
|
+
yes_no: "answer",
|
|
42
|
+
score: "score",
|
|
43
|
+
};
|
|
44
|
+
/** Keys the cell writes next to the answer field; a field may not reuse one. */
|
|
45
|
+
export const COLUMN_DECISION_RESERVED_FIELDS = new Set([
|
|
46
|
+
"confidence",
|
|
47
|
+
"uncertain",
|
|
48
|
+
"reason",
|
|
49
|
+
"probabilities",
|
|
50
|
+
"level",
|
|
51
|
+
]);
|
|
52
|
+
const FIELD_SHAPE = /^[a-z][a-z0-9_]{0,39}$/;
|
|
53
|
+
/** A validation failure naming the field, for the normalizer to wrap in its own error type. */
|
|
54
|
+
export class ColumnDecisionError extends Error {
|
|
55
|
+
field;
|
|
56
|
+
value;
|
|
57
|
+
constructor(message, field, value) {
|
|
58
|
+
super(message);
|
|
59
|
+
this.name = "ColumnDecisionError";
|
|
60
|
+
this.field = field;
|
|
61
|
+
this.value = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** True for any definition that carries a `decision` object, valid or not. */
|
|
65
|
+
export function hasColumnDecision(definition) {
|
|
66
|
+
return isRecord(definition) && isRecord(definition.decision);
|
|
67
|
+
}
|
|
68
|
+
/** The stored decision, or null. Assumes the normalizer already validated it. */
|
|
69
|
+
export function readColumnDecision(definition) {
|
|
70
|
+
if (!isRecord(definition) || !isRecord(definition.decision))
|
|
71
|
+
return null;
|
|
72
|
+
const decision = definition.decision;
|
|
73
|
+
if (decision.engine !== COLUMN_DECISION_ENGINE)
|
|
74
|
+
return null;
|
|
75
|
+
if (!COLUMN_DECISION_MODES.includes(decision.mode))
|
|
76
|
+
return null;
|
|
77
|
+
const mode = decision.mode;
|
|
78
|
+
const field = typeof decision.field === "string" && FIELD_SHAPE.test(decision.field)
|
|
79
|
+
? decision.field
|
|
80
|
+
: COLUMN_DECISION_DEFAULT_FIELD[mode];
|
|
81
|
+
const minConfidence = typeof decision.minConfidence === "number" && Number.isFinite(decision.minConfidence)
|
|
82
|
+
? decision.minConfidence
|
|
83
|
+
: COLUMN_DECISION_DEFAULT_MIN_CONFIDENCE[mode];
|
|
84
|
+
const grounding = decision.grounding === "workspace" ? "workspace" : "none";
|
|
85
|
+
const strings = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
86
|
+
return {
|
|
87
|
+
engine: COLUMN_DECISION_ENGINE,
|
|
88
|
+
mode,
|
|
89
|
+
field,
|
|
90
|
+
...(mode === "labels" ? { labels: strings(decision.labels) } : {}),
|
|
91
|
+
...(mode === "score" ? { levels: strings(decision.levels) } : {}),
|
|
92
|
+
minConfidence,
|
|
93
|
+
grounding,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function readOptionList(value, field, bounds) {
|
|
97
|
+
const raw = typeof value === "string" ? value.split(",") : value;
|
|
98
|
+
if (!Array.isArray(raw)) {
|
|
99
|
+
throw new ColumnDecisionError(`decision.${field} must be a list of strings.`, `decision.${field}`, value);
|
|
100
|
+
}
|
|
101
|
+
const options = [];
|
|
102
|
+
const seen = new Set();
|
|
103
|
+
for (const entry of raw) {
|
|
104
|
+
if (typeof entry !== "string") {
|
|
105
|
+
throw new ColumnDecisionError(`decision.${field} must contain only strings.`, `decision.${field}`, entry);
|
|
106
|
+
}
|
|
107
|
+
const option = entry.trim();
|
|
108
|
+
if (!option)
|
|
109
|
+
continue;
|
|
110
|
+
if (option.length > COLUMN_DECISION_MAX_OPTION_CHARS) {
|
|
111
|
+
throw new ColumnDecisionError(`Each of decision.${field} must be at most ${COLUMN_DECISION_MAX_OPTION_CHARS} characters.`, `decision.${field}`, option);
|
|
112
|
+
}
|
|
113
|
+
const folded = option.toLowerCase();
|
|
114
|
+
if (seen.has(folded)) {
|
|
115
|
+
throw new ColumnDecisionError(`decision.${field} lists "${option}" twice.`, `decision.${field}`, option);
|
|
116
|
+
}
|
|
117
|
+
seen.add(folded);
|
|
118
|
+
options.push(option);
|
|
119
|
+
}
|
|
120
|
+
if (options.length < bounds.min || options.length > bounds.max) {
|
|
121
|
+
throw new ColumnDecisionError(`decision.${field} needs ${bounds.min}–${bounds.max} entries (got ${options.length}).`, `decision.${field}`, options);
|
|
122
|
+
}
|
|
123
|
+
return options;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Validate and fill a caller-supplied decision. Throws {@link ColumnDecisionError}
|
|
127
|
+
* naming the bad field; the caller maps it to its own error envelope.
|
|
128
|
+
*/
|
|
129
|
+
export function normalizeColumnDecision(value) {
|
|
130
|
+
if (!isRecord(value)) {
|
|
131
|
+
throw new ColumnDecisionError("decision must be an object.", "decision", value);
|
|
132
|
+
}
|
|
133
|
+
const engine = value.engine ?? COLUMN_DECISION_ENGINE;
|
|
134
|
+
if (engine !== COLUMN_DECISION_ENGINE) {
|
|
135
|
+
throw new ColumnDecisionError(`decision.engine must be "${COLUMN_DECISION_ENGINE}".`, "decision.engine", engine);
|
|
136
|
+
}
|
|
137
|
+
const mode = value.mode;
|
|
138
|
+
if (!COLUMN_DECISION_MODES.includes(mode)) {
|
|
139
|
+
throw new ColumnDecisionError(`decision.mode must be one of ${COLUMN_DECISION_MODES.join(", ")}.`, "decision.mode", mode);
|
|
140
|
+
}
|
|
141
|
+
const decisionMode = mode;
|
|
142
|
+
const rawField = value.field;
|
|
143
|
+
let field = COLUMN_DECISION_DEFAULT_FIELD[decisionMode];
|
|
144
|
+
if (rawField !== undefined && rawField !== null) {
|
|
145
|
+
if (typeof rawField !== "string" || !FIELD_SHAPE.test(rawField.trim())) {
|
|
146
|
+
throw new ColumnDecisionError("decision.field must be a lowercase key (letters, digits, underscores; starts with a letter; at most 40 characters).", "decision.field", rawField);
|
|
147
|
+
}
|
|
148
|
+
field = rawField.trim();
|
|
149
|
+
}
|
|
150
|
+
if (COLUMN_DECISION_RESERVED_FIELDS.has(field)) {
|
|
151
|
+
throw new ColumnDecisionError(`decision.field cannot be "${field}": the cell already writes that key.`, "decision.field", field);
|
|
152
|
+
}
|
|
153
|
+
const floorMin = decisionMode === "yes_no" ? 0.5 : 0.05;
|
|
154
|
+
let minConfidence = COLUMN_DECISION_DEFAULT_MIN_CONFIDENCE[decisionMode];
|
|
155
|
+
if (value.minConfidence !== undefined && value.minConfidence !== null) {
|
|
156
|
+
const raw = value.minConfidence;
|
|
157
|
+
if (typeof raw !== "number" || !Number.isFinite(raw) || raw < floorMin || raw > 1) {
|
|
158
|
+
throw new ColumnDecisionError(`decision.minConfidence must be a number from ${floorMin} to 1${decisionMode === "yes_no" ? " for yes/no (0.7 means yes at 70% or more, no at 30% or less)" : ""}.`, "decision.minConfidence", raw);
|
|
159
|
+
}
|
|
160
|
+
minConfidence = raw;
|
|
161
|
+
}
|
|
162
|
+
// Default `none` (2026-09-25): a classification judges the row against the
|
|
163
|
+
// column's own question. Workspace knowledge is opt-in, because the ICP slice
|
|
164
|
+
// turned "is this SaaS?" into "is this our ICP?" in the blind acceptance run
|
|
165
|
+
// (Stripe came back "other: not a B2B SaaS targeting seed founders").
|
|
166
|
+
const grounding = value.grounding ?? "none";
|
|
167
|
+
if (!COLUMN_DECISION_GROUNDINGS.includes(grounding)) {
|
|
168
|
+
throw new ColumnDecisionError(`decision.grounding must be one of ${COLUMN_DECISION_GROUNDINGS.join(", ")}.`, "decision.grounding", grounding);
|
|
169
|
+
}
|
|
170
|
+
if (decisionMode === "labels") {
|
|
171
|
+
if (value.levels !== undefined) {
|
|
172
|
+
throw new ColumnDecisionError("decision.levels belongs to score mode; labels mode takes decision.labels.", "decision.levels");
|
|
173
|
+
}
|
|
174
|
+
const labels = readOptionList(value.labels, "labels", {
|
|
175
|
+
min: COLUMN_DECISION_MIN_LABELS,
|
|
176
|
+
max: COLUMN_DECISION_MAX_LABELS,
|
|
177
|
+
});
|
|
178
|
+
return { engine: COLUMN_DECISION_ENGINE, mode: decisionMode, field, labels, minConfidence, grounding: grounding };
|
|
179
|
+
}
|
|
180
|
+
if (decisionMode === "score") {
|
|
181
|
+
if (value.labels !== undefined) {
|
|
182
|
+
throw new ColumnDecisionError("decision.labels belongs to labels mode; score mode takes decision.levels, lowest first.", "decision.labels");
|
|
183
|
+
}
|
|
184
|
+
const levels = readOptionList(value.levels, "levels", {
|
|
185
|
+
min: COLUMN_DECISION_MIN_LEVELS,
|
|
186
|
+
max: COLUMN_DECISION_MAX_LEVELS,
|
|
187
|
+
});
|
|
188
|
+
return { engine: COLUMN_DECISION_ENGINE, mode: decisionMode, field, levels, minConfidence, grounding: grounding };
|
|
189
|
+
}
|
|
190
|
+
if (value.labels !== undefined || value.levels !== undefined) {
|
|
191
|
+
throw new ColumnDecisionError("yes_no mode takes no labels or levels.", value.labels !== undefined ? "decision.labels" : "decision.levels");
|
|
192
|
+
}
|
|
193
|
+
return { engine: COLUMN_DECISION_ENGINE, mode: decisionMode, field, minConfidence, grounding: grounding };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The cell's JSON Schema, derived from the decision so filters, sorts, sub-columns
|
|
197
|
+
* and `{{column.field}}` references see a typed answer: an enum for labels, a
|
|
198
|
+
* boolean for yes/no, a number for a score.
|
|
199
|
+
*/
|
|
200
|
+
export function columnDecisionOutputSchema(decision) {
|
|
201
|
+
const answer = decision.mode === "labels"
|
|
202
|
+
? {
|
|
203
|
+
type: ["string", "null"],
|
|
204
|
+
enum: [...(decision.labels ?? []), null],
|
|
205
|
+
description: "The chosen label (the best guess when uncertain).",
|
|
206
|
+
}
|
|
207
|
+
: decision.mode === "yes_no"
|
|
208
|
+
? { type: ["boolean", "null"], description: "Yes or no (the best guess when uncertain)." }
|
|
209
|
+
: {
|
|
210
|
+
type: ["number", "null"],
|
|
211
|
+
description: `Position on the scale, 0 (${decision.levels?.[0] ?? "lowest"}) to ${Math.max(0, (decision.levels?.length ?? 1) - 1)} (${decision.levels?.at(-1) ?? "highest"}).`,
|
|
212
|
+
};
|
|
213
|
+
const properties = {
|
|
214
|
+
[decision.field]: answer,
|
|
215
|
+
...(decision.mode === "score"
|
|
216
|
+
? { level: { type: ["string", "null"], enum: [...(decision.levels ?? []), null], description: "The nearest level." } }
|
|
217
|
+
: {}),
|
|
218
|
+
confidence: { type: "number", description: "How sure the classifier is, 0 to 1." },
|
|
219
|
+
uncertain: { type: "boolean", description: "True when confidence is below the column's floor." },
|
|
220
|
+
reason: { type: "string", description: "The answer and its confidence in words." },
|
|
221
|
+
};
|
|
222
|
+
return {
|
|
223
|
+
type: "object",
|
|
224
|
+
additionalProperties: true,
|
|
225
|
+
properties,
|
|
226
|
+
required: [decision.field, "confidence", "uncertain", "reason"],
|
|
227
|
+
};
|
|
228
|
+
}
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
*/
|
|
29
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-]+/)
|
|
@@ -0,0 +1,113 @@
|
|
|
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 what it says
|
|
59
|
+
* about the company. Every lane-priced relationship, funding, signal,
|
|
60
|
+
* technology and web-presence field is opt-in through `--fields`, so the
|
|
61
|
+
* default bundle's per-row ceiling stays the profile chain's.
|
|
62
|
+
*
|
|
63
|
+
* Funding stage and latest round left the default on 2026-09-24 (founder, D55):
|
|
64
|
+
* the profile lane is Blitz first now, and Blitz's company profile carries no
|
|
65
|
+
* funding, so on the rows Blitz answers those two columns would sit empty. They
|
|
66
|
+
* live in the Funding details group instead.
|
|
67
|
+
*/
|
|
68
|
+
export declare const DEFAULT_COMPANY_PRESET_FIELDS: readonly CompanyEnrichmentFieldKey[];
|
|
69
|
+
export type CompanyEnrichmentFieldGroupId = "profile_details" | "funding_details" | "job_openings" | "tech_stack" | "technology_check";
|
|
70
|
+
export type CompanyEnrichmentFieldGroup = {
|
|
71
|
+
id: CompanyEnrichmentFieldGroupId;
|
|
72
|
+
/** Picker row and catalog label, in customer words. */
|
|
73
|
+
label: string;
|
|
74
|
+
/** ONE founder-voice line. No provider names. */
|
|
75
|
+
description: string;
|
|
76
|
+
/** The catalog fields this group adds to the company bundle, in column order. */
|
|
77
|
+
fields: readonly CompanyEnrichmentFieldKey[];
|
|
78
|
+
/**
|
|
79
|
+
* What the group does to the per-row bill.
|
|
80
|
+
*
|
|
81
|
+
* `included` — every field is read out of the company profile the default
|
|
82
|
+
* bundle already fetches, so ticking it adds columns and no credits.
|
|
83
|
+
* `extra` — at least one field has its own provider lookup, so the run can
|
|
84
|
+
* bill more per row. The exact number depends on which lane answers, which
|
|
85
|
+
* only a dry run over the real rows can say; no surface quotes one here.
|
|
86
|
+
*/
|
|
87
|
+
cost: "included" | "extra";
|
|
88
|
+
/** Set when the group cannot be added without extra caller input. */
|
|
89
|
+
requires?: "check_technologies";
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* The opt-in company fields, grouped the way a customer asks for them ("add
|
|
93
|
+
* competitors", "add their tech stack"). Every surface that offers a company
|
|
94
|
+
* field beyond the default bundle reads THIS list: the enrichment column catalog
|
|
95
|
+
* (`oxygen enrichment catalog`, `oxygen_enrichment_catalog`), the web add-column
|
|
96
|
+
* picker, and the catalog pricing. Adding a group adds its fields to the SAME
|
|
97
|
+
* company enrichment column — re-applying the bundle widens the existing
|
|
98
|
+
* column's field list instead of creating a second paid column.
|
|
99
|
+
*/
|
|
100
|
+
export declare const COMPANY_ENRICHMENT_FIELD_GROUPS: readonly CompanyEnrichmentFieldGroup[];
|
|
101
|
+
export declare function companyEnrichmentFieldGroup(id: string): CompanyEnrichmentFieldGroup | null;
|
|
102
|
+
/**
|
|
103
|
+
* Parse a comma-separated or array field list against the catalog. Returns the
|
|
104
|
+
* unique, lowercased keys in request order, or throws naming every unknown
|
|
105
|
+
* entry with the supported list — never a silent fallback to a default set,
|
|
106
|
+
* which is how `--fields tech,hiring,profile` once ran the four default fields
|
|
107
|
+
* and billed for an answer nobody asked for (OXP-7.2.9).
|
|
108
|
+
*/
|
|
109
|
+
export declare function parseCompanyEnrichmentFieldList(value: unknown): {
|
|
110
|
+
fields: CompanyEnrichmentFieldKey[];
|
|
111
|
+
unknown: string[];
|
|
112
|
+
coming_soon: CompanyEnrichmentFieldKey[];
|
|
113
|
+
};
|