@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function errorMessage(error: unknown): string;
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
// idempotent, never throws, and never widens a message.
|
|
21
21
|
import { redactSecretsInString } from "./redaction.js";
|
|
22
22
|
import { isRetryableConcurrencyError, redactSqlParameters } from "./sql-error.js";
|
|
23
|
+
import { isRecord } from "./type-guards.js";
|
|
23
24
|
// Whole fields whose NAME marks them secret. Unchanged from the operation-event
|
|
24
25
|
// writer this was extracted from; substring credentials (Bearer/sk-/DB URLs) are
|
|
25
26
|
// scrubbed separately by redactSecretsInString.
|
|
@@ -306,6 +307,3 @@ export function summarizeProviderBody(body) {
|
|
|
306
307
|
function truncate(value, maxLength) {
|
|
307
308
|
return value.length > maxLength ? `${value.slice(0, maxLength - 3)}...` : value;
|
|
308
309
|
}
|
|
309
|
-
function isRecord(value) {
|
|
310
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
311
|
-
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type ExternalWritePolicy = "allow" | "deny";
|
|
2
|
+
/** The error code every refusal carries, on every surface. */
|
|
3
|
+
export declare const EXTERNAL_WRITES_DENIED_CODE = "external_writes_denied";
|
|
4
|
+
export declare const EXTERNAL_WRITES_ENV_VAR = "OXYGEN_EXTERNAL_WRITES";
|
|
5
|
+
type PolicyEnv = {
|
|
6
|
+
[key: string]: string | undefined;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* `deny` when `OXYGEN_EXTERNAL_WRITES` is exactly `deny` (trimmed,
|
|
10
|
+
* case-insensitive), `allow` for anything else including unset.
|
|
11
|
+
*
|
|
12
|
+
* Read per call rather than memoized at module load: the value is read on paths
|
|
13
|
+
* a test needs to drive both ways, and a process-static snapshot would make the
|
|
14
|
+
* denial untestable in exactly the suite that has to prove it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveExternalWritePolicy(env?: PolicyEnv): ExternalWritePolicy;
|
|
17
|
+
/** Convenience predicate for callers that only branch. */
|
|
18
|
+
export declare function externalWritesDenied(env?: PolicyEnv): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Refuse `action` when this environment denies external writes.
|
|
21
|
+
*
|
|
22
|
+
* Throws `OxygenError` with code `external_writes_denied` so the refusal travels
|
|
23
|
+
* the CLI/MCP envelope, the run record and the worker's job failure the same way
|
|
24
|
+
* every other refusal does — a shadow that swallows the denial into a generic
|
|
25
|
+
* failure teaches its operator nothing.
|
|
26
|
+
*
|
|
27
|
+
* Logged at `warn`, not `error`: a refusal here is the control WORKING, and the
|
|
28
|
+
* shadow's soak is expected to produce a stream of them. The message names both
|
|
29
|
+
* the action and the variable, because the person reading it in Axiom is usually
|
|
30
|
+
* asking "why did nothing send" and the answer is a configuration they set.
|
|
31
|
+
*/
|
|
32
|
+
export declare function assertExternalWritesAllowed(action: string, env?: PolicyEnv): void;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// The environment-level external-write denial, and the one place that reads
|
|
2
|
+
// `OXYGEN_EXTERNAL_WRITES`.
|
|
3
|
+
//
|
|
4
|
+
// WHY THIS EXISTS AND WHY IT IS NOT A CLASSIFIER. A production *shadow* holds a
|
|
5
|
+
// restored copy of production: real mailbox OAuth tokens, real provider
|
|
6
|
+
// credentials, real customer records — while managed production is still live
|
|
7
|
+
// and doing the same work. The restore script scrubs those columns, but a scrub
|
|
8
|
+
// is data hygiene, not a guarantee: one un-nulled column is one real send, to a
|
|
9
|
+
// real prospect, twice. So the shadow also fails closed at the platform
|
|
10
|
+
// boundary (`.agents/skills/oxygen-self-host-cutover`, Part 1 Control 3).
|
|
11
|
+
//
|
|
12
|
+
// THE ORDERING IS THE SAFETY PROPERTY, and it is the same lesson
|
|
13
|
+
// `EXTERNAL_REACH_CAPABILITY_NAMES` (packages/copilot/src/policy.ts) was written
|
|
14
|
+
// to record: `capabilityLooksPaid` read ARGUMENT NAMES, so a write-capable tool
|
|
15
|
+
// declaring `max_credits` classified `paid` and could never reach the
|
|
16
|
+
// `external_write` branch — which made a real outbound launch waivable by an
|
|
17
|
+
// approval-free spend budget on a declared ceiling of one credit. A denial that
|
|
18
|
+
// runs anywhere except FIRST inherits exactly that defect. Every caller here
|
|
19
|
+
// asks this before it classifies, before it evaluates an allowance, an approval
|
|
20
|
+
// or a policy rule, and before it constructs a provider client.
|
|
21
|
+
//
|
|
22
|
+
// UNSET MEANS ALLOW, deliberately and permanently. Managed production must be
|
|
23
|
+
// byte-identical to the day before this module landed; only a shadow sets the
|
|
24
|
+
// variable. `deny` is the only recognised denial value — a typo (`denied`,
|
|
25
|
+
// `true`, `1`) allows, which is the fail-OPEN direction and is the right one
|
|
26
|
+
// here: a variable nobody set must never take production off the air, and the
|
|
27
|
+
// shadow's operator proves the denial by ATTEMPTING a send and observing the
|
|
28
|
+
// refusal rather than by trusting the configuration (same skill, "Proving it,
|
|
29
|
+
// rather than believing it").
|
|
30
|
+
import { OxygenError } from "./cli-result.js";
|
|
31
|
+
import { log } from "./log.js";
|
|
32
|
+
/** The error code every refusal carries, on every surface. */
|
|
33
|
+
export const EXTERNAL_WRITES_DENIED_CODE = "external_writes_denied";
|
|
34
|
+
export const EXTERNAL_WRITES_ENV_VAR = "OXYGEN_EXTERNAL_WRITES";
|
|
35
|
+
/**
|
|
36
|
+
* `deny` when `OXYGEN_EXTERNAL_WRITES` is exactly `deny` (trimmed,
|
|
37
|
+
* case-insensitive), `allow` for anything else including unset.
|
|
38
|
+
*
|
|
39
|
+
* Read per call rather than memoized at module load: the value is read on paths
|
|
40
|
+
* a test needs to drive both ways, and a process-static snapshot would make the
|
|
41
|
+
* denial untestable in exactly the suite that has to prove it.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveExternalWritePolicy(env = process.env) {
|
|
44
|
+
return env[EXTERNAL_WRITES_ENV_VAR]?.trim().toLowerCase() === "deny" ? "deny" : "allow";
|
|
45
|
+
}
|
|
46
|
+
/** Convenience predicate for callers that only branch. */
|
|
47
|
+
export function externalWritesDenied(env = process.env) {
|
|
48
|
+
return resolveExternalWritePolicy(env) === "deny";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Refuse `action` when this environment denies external writes.
|
|
52
|
+
*
|
|
53
|
+
* Throws `OxygenError` with code `external_writes_denied` so the refusal travels
|
|
54
|
+
* the CLI/MCP envelope, the run record and the worker's job failure the same way
|
|
55
|
+
* every other refusal does — a shadow that swallows the denial into a generic
|
|
56
|
+
* failure teaches its operator nothing.
|
|
57
|
+
*
|
|
58
|
+
* Logged at `warn`, not `error`: a refusal here is the control WORKING, and the
|
|
59
|
+
* shadow's soak is expected to produce a stream of them. The message names both
|
|
60
|
+
* the action and the variable, because the person reading it in Axiom is usually
|
|
61
|
+
* asking "why did nothing send" and the answer is a configuration they set.
|
|
62
|
+
*/
|
|
63
|
+
export function assertExternalWritesAllowed(action, env = process.env) {
|
|
64
|
+
if (resolveExternalWritePolicy(env) !== "deny")
|
|
65
|
+
return;
|
|
66
|
+
log("warn", "external_writes.denied", { action });
|
|
67
|
+
throw new OxygenError(EXTERNAL_WRITES_DENIED_CODE, `External writes are denied in this environment (${EXTERNAL_WRITES_ENV_VAR}=deny), so "${action}" cannot run.`, { details: { action, env_var: EXTERNAL_WRITES_ENV_VAR } });
|
|
68
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical percentage formatting shared by the web app and MCP surfaces.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format a 0..1 ratio as an en-US percentage with at most one decimal; `null`
|
|
6
|
+
* renders as an em dash. The locale is pinned so web and MCP cannot drift.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatPercent(value: number | null): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical percentage formatting shared by the web app and MCP surfaces.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format a 0..1 ratio as an en-US percentage with at most one decimal; `null`
|
|
6
|
+
* renders as an em dash. The locale is pinned so web and MCP cannot drift.
|
|
7
|
+
*/
|
|
8
|
+
export function formatPercent(value) {
|
|
9
|
+
return value === null ? "—" : new Intl.NumberFormat("en-US", {
|
|
10
|
+
style: "percent",
|
|
11
|
+
maximumFractionDigits: 1,
|
|
12
|
+
}).format(value);
|
|
13
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consumer / freemail mail hosts — the ONE list, in two deliberate tiers.
|
|
3
|
+
*
|
|
4
|
+
* EIGHT call sites used to carry their own hand-typed copy of this concept — 101, 34, 34,
|
|
5
|
+
* 25, 22, 18, 13 and 12 entries — none importing another, none covered by a test
|
|
6
|
+
* asserting they agreed, and two of them byte-identical forks that nothing kept in sync.
|
|
7
|
+
* The same address resolved differently depending on which copy a code path happened to
|
|
8
|
+
* reach. This module is the single source of truth; `packages/shared` is the only leaf
|
|
9
|
+
* package all eight can reach without inverting a dependency edge.
|
|
10
|
+
*
|
|
11
|
+
* WHY TWO TIERS, and why they must not be collapsed into one:
|
|
12
|
+
*
|
|
13
|
+
* The callers are not asking the same question, and the cost of a wrong answer is
|
|
14
|
+
* asymmetric in opposite directions.
|
|
15
|
+
*
|
|
16
|
+
* - `FREEMAIL_DOMAINS` (core) answers "may I treat this domain as a shared EMPLOYER?"
|
|
17
|
+
* Two consumers ride on that answer, and they fail in OPPOSITE directions, which is
|
|
18
|
+
* why this tier is conservative rather than generous:
|
|
19
|
+
*
|
|
20
|
+
* 1. The company-scope reply stop (`settings.stop_on_reply_scope = "company_domain"`)
|
|
21
|
+
* terminalizes an enrollment's colleagues by matching domain. A host wrongly IN
|
|
22
|
+
* this tier is harmless here; a host wrongly OUT of it stops strangers who merely
|
|
23
|
+
* share a mail host — real, customer-visible damage.
|
|
24
|
+
* 2. Company derivation from a person's email domain
|
|
25
|
+
* (`deriveCompanyDomainFromEmail`, `packages/tenant-db/src/table-link-plan.ts`)
|
|
26
|
+
* drops freemail before offering a domain as a company join key. Here the
|
|
27
|
+
* polarity REVERSES: a host wrongly OUT of this tier makes OXYGEN mint a company
|
|
28
|
+
* record for a mail provider, so every @<host> person collapses onto one fake
|
|
29
|
+
* account. Where that derivation is wired into record creation, removing a host
|
|
30
|
+
* from this tier silently starts creating accounts for it.
|
|
31
|
+
*
|
|
32
|
+
* So a host must be in this tier to be safe for BOTH, and the safe direction for an
|
|
33
|
+
* edit is to ADD, never to remove or relocate to the broad tier. Extend it only with
|
|
34
|
+
* hosts that are unambiguously consumer mailboxes — and note the ORDER caveat in
|
|
35
|
+
* `packages/tenant-db/src/freemail-domains.ts`, because a core-tier edit also changes
|
|
36
|
+
* a formula string already persisted in every tenant's column definitions.
|
|
37
|
+
*
|
|
38
|
+
* - `PERSONAL_EMAIL_DOMAINS` (broad) answers "is this domain a poor stand-in for a
|
|
39
|
+
* COMPANY identity?" — used to skip fetching a workspace icon, to reject a personal
|
|
40
|
+
* host typed into the marketing hero form, to avoid picking a personal address as a
|
|
41
|
+
* person's work email, and to avoid treating a meeting participant's mail host as their
|
|
42
|
+
* employer. Here a false positive costs nothing (we simply decline to guess a company)
|
|
43
|
+
* while a miss produces a junk company/icon. So this tier is the superset, including
|
|
44
|
+
* regional and privacy-focused providers.
|
|
45
|
+
*
|
|
46
|
+
* The broad tier is a strict superset of the core tier by construction below, so a host
|
|
47
|
+
* can never be "freemail but not personal" — exactly the incoherence the forks had
|
|
48
|
+
* developed (the old core list carried `t-online.de`; several broad lists did not).
|
|
49
|
+
*
|
|
50
|
+
* The broad tier is the union of all eight former lists. Most of its bulk comes from the
|
|
51
|
+
* old `packages/formula` copy, which was by far the best maintained and is the only one
|
|
52
|
+
* that covered country-specific variants (`yahoo.co.uk`, `hotmail.fr`, `orange.fr`,
|
|
53
|
+
* `wp.pl`, `uol.com.br` …) and ISP mailboxes (`comcast.net`, `btinternet.com`). Every
|
|
54
|
+
* other caller was silently missing those.
|
|
55
|
+
*/
|
|
56
|
+
/** Tier 1 — see the module note. Small on purpose. */
|
|
57
|
+
export declare const FREEMAIL_DOMAINS: ReadonlySet<string>;
|
|
58
|
+
/** Tier 2 — a strict superset of {@link FREEMAIL_DOMAINS}. */
|
|
59
|
+
export declare const PERSONAL_EMAIL_DOMAINS: ReadonlySet<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Reduce an address or a bare domain to a comparable mail host: lowercased, trimmed,
|
|
62
|
+
* everything before the last '@' dropped, and a trailing FQDN dot removed. Returns null
|
|
63
|
+
* for junk/empty/nullish input so callers can distinguish "not a host" from "not in the
|
|
64
|
+
* set". Previously each fork normalized differently — one did full address handling, two
|
|
65
|
+
* only lowercased a bare domain — so the same input could resolve differently depending
|
|
66
|
+
* on which copy you happened to call.
|
|
67
|
+
*/
|
|
68
|
+
export declare function normalizeMailHost(value: string | null | undefined): string | null;
|
|
69
|
+
/**
|
|
70
|
+
* True when the host is a tier-1 consumer mailbox, i.e. it NEVER identifies a shared
|
|
71
|
+
* employer. Accepts a bare domain or a full address. Unknown input returns false: an
|
|
72
|
+
* unrecognized domain is treated as a company domain, which keeps the company-scope
|
|
73
|
+
* stop's safe default identical to lead-scope.
|
|
74
|
+
*/
|
|
75
|
+
export declare function isFreemailDomain(domain: string | null | undefined): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* True when the host is a personal mailbox of any kind (tier 2). Use this for "should I
|
|
78
|
+
* infer a company from this domain?"; use {@link isFreemailDomain} when a false positive
|
|
79
|
+
* would wrongly group unrelated people as colleagues.
|
|
80
|
+
*/
|
|
81
|
+
export declare function isPersonalEmailDomain(domain: string | null | undefined): boolean;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consumer / freemail mail hosts — the ONE list, in two deliberate tiers.
|
|
3
|
+
*
|
|
4
|
+
* EIGHT call sites used to carry their own hand-typed copy of this concept — 101, 34, 34,
|
|
5
|
+
* 25, 22, 18, 13 and 12 entries — none importing another, none covered by a test
|
|
6
|
+
* asserting they agreed, and two of them byte-identical forks that nothing kept in sync.
|
|
7
|
+
* The same address resolved differently depending on which copy a code path happened to
|
|
8
|
+
* reach. This module is the single source of truth; `packages/shared` is the only leaf
|
|
9
|
+
* package all eight can reach without inverting a dependency edge.
|
|
10
|
+
*
|
|
11
|
+
* WHY TWO TIERS, and why they must not be collapsed into one:
|
|
12
|
+
*
|
|
13
|
+
* The callers are not asking the same question, and the cost of a wrong answer is
|
|
14
|
+
* asymmetric in opposite directions.
|
|
15
|
+
*
|
|
16
|
+
* - `FREEMAIL_DOMAINS` (core) answers "may I treat this domain as a shared EMPLOYER?"
|
|
17
|
+
* Two consumers ride on that answer, and they fail in OPPOSITE directions, which is
|
|
18
|
+
* why this tier is conservative rather than generous:
|
|
19
|
+
*
|
|
20
|
+
* 1. The company-scope reply stop (`settings.stop_on_reply_scope = "company_domain"`)
|
|
21
|
+
* terminalizes an enrollment's colleagues by matching domain. A host wrongly IN
|
|
22
|
+
* this tier is harmless here; a host wrongly OUT of it stops strangers who merely
|
|
23
|
+
* share a mail host — real, customer-visible damage.
|
|
24
|
+
* 2. Company derivation from a person's email domain
|
|
25
|
+
* (`deriveCompanyDomainFromEmail`, `packages/tenant-db/src/table-link-plan.ts`)
|
|
26
|
+
* drops freemail before offering a domain as a company join key. Here the
|
|
27
|
+
* polarity REVERSES: a host wrongly OUT of this tier makes OXYGEN mint a company
|
|
28
|
+
* record for a mail provider, so every @<host> person collapses onto one fake
|
|
29
|
+
* account. Where that derivation is wired into record creation, removing a host
|
|
30
|
+
* from this tier silently starts creating accounts for it.
|
|
31
|
+
*
|
|
32
|
+
* So a host must be in this tier to be safe for BOTH, and the safe direction for an
|
|
33
|
+
* edit is to ADD, never to remove or relocate to the broad tier. Extend it only with
|
|
34
|
+
* hosts that are unambiguously consumer mailboxes — and note the ORDER caveat in
|
|
35
|
+
* `packages/tenant-db/src/freemail-domains.ts`, because a core-tier edit also changes
|
|
36
|
+
* a formula string already persisted in every tenant's column definitions.
|
|
37
|
+
*
|
|
38
|
+
* - `PERSONAL_EMAIL_DOMAINS` (broad) answers "is this domain a poor stand-in for a
|
|
39
|
+
* COMPANY identity?" — used to skip fetching a workspace icon, to reject a personal
|
|
40
|
+
* host typed into the marketing hero form, to avoid picking a personal address as a
|
|
41
|
+
* person's work email, and to avoid treating a meeting participant's mail host as their
|
|
42
|
+
* employer. Here a false positive costs nothing (we simply decline to guess a company)
|
|
43
|
+
* while a miss produces a junk company/icon. So this tier is the superset, including
|
|
44
|
+
* regional and privacy-focused providers.
|
|
45
|
+
*
|
|
46
|
+
* The broad tier is a strict superset of the core tier by construction below, so a host
|
|
47
|
+
* can never be "freemail but not personal" — exactly the incoherence the forks had
|
|
48
|
+
* developed (the old core list carried `t-online.de`; several broad lists did not).
|
|
49
|
+
*
|
|
50
|
+
* The broad tier is the union of all eight former lists. Most of its bulk comes from the
|
|
51
|
+
* old `packages/formula` copy, which was by far the best maintained and is the only one
|
|
52
|
+
* that covered country-specific variants (`yahoo.co.uk`, `hotmail.fr`, `orange.fr`,
|
|
53
|
+
* `wp.pl`, `uol.com.br` …) and ISP mailboxes (`comcast.net`, `btinternet.com`). Every
|
|
54
|
+
* other caller was silently missing those.
|
|
55
|
+
*/
|
|
56
|
+
// Tier 1. Unambiguous global consumer mailboxes. Load-bearing for the company-scope
|
|
57
|
+
// reply stop — read the note above before adding anything here.
|
|
58
|
+
const CORE_FREEMAIL_DOMAINS = [
|
|
59
|
+
"gmail.com",
|
|
60
|
+
"googlemail.com",
|
|
61
|
+
"outlook.com",
|
|
62
|
+
"hotmail.com",
|
|
63
|
+
"live.com",
|
|
64
|
+
"msn.com",
|
|
65
|
+
"yahoo.com",
|
|
66
|
+
"ymail.com",
|
|
67
|
+
"icloud.com",
|
|
68
|
+
"me.com",
|
|
69
|
+
"aol.com",
|
|
70
|
+
"gmx.com",
|
|
71
|
+
"gmx.net",
|
|
72
|
+
"gmx.de",
|
|
73
|
+
"web.de",
|
|
74
|
+
"proton.me",
|
|
75
|
+
"protonmail.com",
|
|
76
|
+
"mail.com",
|
|
77
|
+
"zoho.com",
|
|
78
|
+
"yandex.com",
|
|
79
|
+
"yandex.ru",
|
|
80
|
+
"t-online.de",
|
|
81
|
+
];
|
|
82
|
+
// Tier 2 additions: country variants of the global providers, regional hosts (qq.com,
|
|
83
|
+
// naver.com, wp.pl, libero.it, uol.com.br), ISP mailboxes (comcast.net, btinternet.com,
|
|
84
|
+
// telenet.be), privacy hosts (pm.me, tutanota.com, duck.com) and prosumer ones
|
|
85
|
+
// (fastmail, hey.com, mac.com). Confident enough to refuse to call any of them a
|
|
86
|
+
// company; NOT confident enough to let them drive a reply-stop decision — an ISP or
|
|
87
|
+
// country-TLD host is exactly where a "shared employer" guess goes wrong.
|
|
88
|
+
// Sorted, because this list is long enough that grouping by theme would rot.
|
|
89
|
+
const ADDITIONAL_PERSONAL_EMAIL_DOMAINS = [
|
|
90
|
+
"126.com", "163.com", "att.net", "bellsouth.net",
|
|
91
|
+
"bigpond.com", "bluewin.ch", "blueyonder.co.uk", "bol.com.br",
|
|
92
|
+
"btinternet.com", "charter.net", "comcast.net", "cox.net",
|
|
93
|
+
"daum.net", "duck.com", "earthlink.net", "fastmail.com",
|
|
94
|
+
"fastmail.fm", "free.fr", "freenet.de", "gmx.at",
|
|
95
|
+
"gmx.ch", "hanmail.net", "hey.com", "home.nl",
|
|
96
|
+
"hotmail.co.uk", "hotmail.de", "hotmail.es", "hotmail.fr",
|
|
97
|
+
"hotmail.it", "interia.pl", "kpnmail.nl", "laposte.net",
|
|
98
|
+
"libero.it", "live.co.uk", "live.de", "live.fr",
|
|
99
|
+
"mac.com", "mail.ru", "naver.com", "o2.pl",
|
|
100
|
+
"onet.pl", "optusnet.com.au", "orange.fr", "outlook.co.uk",
|
|
101
|
+
"outlook.de", "outlook.es", "outlook.fr", "pm.me",
|
|
102
|
+
"qq.com", "rediffmail.com", "rocketmail.com", "rogers.com",
|
|
103
|
+
"sbcglobal.net", "seznam.cz", "sfr.fr", "shaw.ca",
|
|
104
|
+
"sina.com", "sky.com", "skynet.be", "sympatico.ca",
|
|
105
|
+
"talktalk.net", "telenet.be", "telus.net", "terra.com.br",
|
|
106
|
+
"tuta.io", "tutanota.com", "uol.com.br", "verizon.net",
|
|
107
|
+
"virgilio.it", "virginmedia.com", "wanadoo.fr", "wp.pl",
|
|
108
|
+
"yahoo.ca", "yahoo.co.jp", "yahoo.co.uk", "yahoo.com.au",
|
|
109
|
+
"yahoo.de", "yahoo.es", "yahoo.fr", "yahoo.it",
|
|
110
|
+
"ziggo.nl",
|
|
111
|
+
];
|
|
112
|
+
/** Tier 1 — see the module note. Small on purpose. */
|
|
113
|
+
export const FREEMAIL_DOMAINS = new Set(CORE_FREEMAIL_DOMAINS);
|
|
114
|
+
/** Tier 2 — a strict superset of {@link FREEMAIL_DOMAINS}. */
|
|
115
|
+
export const PERSONAL_EMAIL_DOMAINS = new Set([
|
|
116
|
+
...CORE_FREEMAIL_DOMAINS,
|
|
117
|
+
...ADDITIONAL_PERSONAL_EMAIL_DOMAINS,
|
|
118
|
+
]);
|
|
119
|
+
/**
|
|
120
|
+
* Reduce an address or a bare domain to a comparable mail host: lowercased, trimmed,
|
|
121
|
+
* everything before the last '@' dropped, and a trailing FQDN dot removed. Returns null
|
|
122
|
+
* for junk/empty/nullish input so callers can distinguish "not a host" from "not in the
|
|
123
|
+
* set". Previously each fork normalized differently — one did full address handling, two
|
|
124
|
+
* only lowercased a bare domain — so the same input could resolve differently depending
|
|
125
|
+
* on which copy you happened to call.
|
|
126
|
+
*/
|
|
127
|
+
export function normalizeMailHost(value) {
|
|
128
|
+
if (typeof value !== "string")
|
|
129
|
+
return null;
|
|
130
|
+
let host = value.trim().toLowerCase();
|
|
131
|
+
if (!host)
|
|
132
|
+
return null;
|
|
133
|
+
const at = host.lastIndexOf("@");
|
|
134
|
+
if (at >= 0)
|
|
135
|
+
host = host.slice(at + 1);
|
|
136
|
+
host = host.replace(/\.+$/, "");
|
|
137
|
+
return host || null;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* True when the host is a tier-1 consumer mailbox, i.e. it NEVER identifies a shared
|
|
141
|
+
* employer. Accepts a bare domain or a full address. Unknown input returns false: an
|
|
142
|
+
* unrecognized domain is treated as a company domain, which keeps the company-scope
|
|
143
|
+
* stop's safe default identical to lead-scope.
|
|
144
|
+
*/
|
|
145
|
+
export function isFreemailDomain(domain) {
|
|
146
|
+
const host = normalizeMailHost(domain);
|
|
147
|
+
return host !== null && FREEMAIL_DOMAINS.has(host);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* True when the host is a personal mailbox of any kind (tier 2). Use this for "should I
|
|
151
|
+
* infer a company from this domain?"; use {@link isFreemailDomain} when a false positive
|
|
152
|
+
* would wrongly group unrelated people as colleagues.
|
|
153
|
+
*/
|
|
154
|
+
export function isPersonalEmailDomain(domain) {
|
|
155
|
+
const host = normalizeMailHost(domain);
|
|
156
|
+
return host !== null && PERSONAL_EMAIL_DOMAINS.has(host);
|
|
157
|
+
}
|
|
@@ -423,3 +423,4 @@ export declare function normalizeFutureSignupLifecycleProjectionEnvelope(value:
|
|
|
423
423
|
export declare function normalizeFutureSignupLifecycleProjectionV2Envelope(value: unknown): NormalizedFutureSignupLifecycleProjectionV2Envelope;
|
|
424
424
|
export declare function normalizeFutureSignupLifecycleProjectionDestinationApplyOptions(value: unknown): NormalizedFutureSignupLifecycleProjectionDestinationApplyOptions;
|
|
425
425
|
export declare function normalizeFutureSignupLifecycleProjectionV2DestinationApplyOptions(value: unknown): NormalizedFutureSignupLifecycleProjectionV2DestinationApplyOptions;
|
|
426
|
+
export declare function codeUnitCompare(left: string, right: string): number;
|
|
@@ -1336,7 +1336,7 @@ function canonicalize(value) {
|
|
|
1336
1336
|
}
|
|
1337
1337
|
return value;
|
|
1338
1338
|
}
|
|
1339
|
-
function codeUnitCompare(left, right) {
|
|
1339
|
+
export function codeUnitCompare(left, right) {
|
|
1340
1340
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
1341
1341
|
}
|
|
1342
1342
|
function invalid(path, rule) {
|
|
@@ -17,6 +17,10 @@ export * from "./provider-balance-signal.js";
|
|
|
17
17
|
export * from "./provider-funding-errors.js";
|
|
18
18
|
export * from "./publishing-limits.js";
|
|
19
19
|
export * from "./spend-safety.js";
|
|
20
|
+
export * from "./company-enrichment-fields.js";
|
|
21
|
+
export * from "./enrichment-intents.js";
|
|
22
|
+
export * from "./error-message.js";
|
|
23
|
+
export * from "./sql-rows.js";
|
|
20
24
|
export * from "./cell-format.js";
|
|
21
25
|
export * from "./cli-envelope.js";
|
|
22
26
|
export * from "./cli-login-code.js";
|
|
@@ -27,8 +31,10 @@ export * from "./crm-activity-events.js";
|
|
|
27
31
|
export * from "./column-types.js";
|
|
28
32
|
export * from "./copilot-errors.js";
|
|
29
33
|
export * from "./copilot-journeys.js";
|
|
34
|
+
export * from "./copilot-playbooks.js";
|
|
30
35
|
export * from "./copilot-plan.js";
|
|
31
36
|
export * from "./credit-guidance.js";
|
|
37
|
+
export * from "./deploy-env.js";
|
|
32
38
|
export * from "./directory.js";
|
|
33
39
|
export * from "./email-dsn.js";
|
|
34
40
|
export * from "./email-warmup-readiness.js";
|
|
@@ -72,6 +78,7 @@ export * from "./dial-guardrail-overrides.js";
|
|
|
72
78
|
export * from "./sequences.js";
|
|
73
79
|
export * from "./suppression-entries.js";
|
|
74
80
|
export * from "./dnc-identities.js";
|
|
81
|
+
export * from "./dnc-rules.js";
|
|
75
82
|
export * from "./table-limits.js";
|
|
76
83
|
export * from "./table-capacity.js";
|
|
77
84
|
export * from "./log.js";
|
|
@@ -88,8 +95,12 @@ export * from "./tags.js";
|
|
|
88
95
|
export * from "./telemetry.js";
|
|
89
96
|
export * from "./tenant-database-secret.js";
|
|
90
97
|
export * from "./egress-transport-readiness.js";
|
|
98
|
+
export * from "./rate-window.js";
|
|
91
99
|
export * from "./timing.js";
|
|
92
100
|
export * from "./type-guards.js";
|
|
101
|
+
export * from "./value-readers.js";
|
|
102
|
+
export * from "./array-utils.js";
|
|
103
|
+
export * from "./format-percent.js";
|
|
93
104
|
export * from "./worker-failures-queue.js";
|
|
94
105
|
export * from "./workflow-mcp-tools.js";
|
|
95
106
|
export * from "./webhook-headers.js";
|
|
@@ -123,3 +134,6 @@ export * from "./ugc.js";
|
|
|
123
134
|
export * from "./ugc-amplification-identity.js";
|
|
124
135
|
export * from "./knowledge-repository.js";
|
|
125
136
|
export * from "./knowledge-bases.js";
|
|
137
|
+
export * from "./external-write-policy.js";
|
|
138
|
+
export * from "./log-sink-selector.js";
|
|
139
|
+
export * from "./otlp-log-sink.js";
|
|
@@ -17,6 +17,10 @@ export * from "./provider-balance-signal.js";
|
|
|
17
17
|
export * from "./provider-funding-errors.js";
|
|
18
18
|
export * from "./publishing-limits.js";
|
|
19
19
|
export * from "./spend-safety.js";
|
|
20
|
+
export * from "./company-enrichment-fields.js";
|
|
21
|
+
export * from "./enrichment-intents.js";
|
|
22
|
+
export * from "./error-message.js";
|
|
23
|
+
export * from "./sql-rows.js";
|
|
20
24
|
export * from "./cell-format.js";
|
|
21
25
|
export * from "./cli-envelope.js";
|
|
22
26
|
export * from "./cli-login-code.js";
|
|
@@ -27,8 +31,10 @@ export * from "./crm-activity-events.js";
|
|
|
27
31
|
export * from "./column-types.js";
|
|
28
32
|
export * from "./copilot-errors.js";
|
|
29
33
|
export * from "./copilot-journeys.js";
|
|
34
|
+
export * from "./copilot-playbooks.js";
|
|
30
35
|
export * from "./copilot-plan.js";
|
|
31
36
|
export * from "./credit-guidance.js";
|
|
37
|
+
export * from "./deploy-env.js";
|
|
32
38
|
export * from "./directory.js";
|
|
33
39
|
export * from "./email-dsn.js";
|
|
34
40
|
export * from "./email-warmup-readiness.js";
|
|
@@ -72,6 +78,7 @@ export * from "./dial-guardrail-overrides.js";
|
|
|
72
78
|
export * from "./sequences.js";
|
|
73
79
|
export * from "./suppression-entries.js";
|
|
74
80
|
export * from "./dnc-identities.js";
|
|
81
|
+
export * from "./dnc-rules.js";
|
|
75
82
|
export * from "./table-limits.js";
|
|
76
83
|
export * from "./table-capacity.js";
|
|
77
84
|
export * from "./log.js";
|
|
@@ -99,8 +106,12 @@ export * from "./tags.js";
|
|
|
99
106
|
export * from "./telemetry.js";
|
|
100
107
|
export * from "./tenant-database-secret.js";
|
|
101
108
|
export * from "./egress-transport-readiness.js";
|
|
109
|
+
export * from "./rate-window.js";
|
|
102
110
|
export * from "./timing.js";
|
|
103
111
|
export * from "./type-guards.js";
|
|
112
|
+
export * from "./value-readers.js";
|
|
113
|
+
export * from "./array-utils.js";
|
|
114
|
+
export * from "./format-percent.js";
|
|
104
115
|
export * from "./worker-failures-queue.js";
|
|
105
116
|
export * from "./workflow-mcp-tools.js";
|
|
106
117
|
export * from "./webhook-headers.js";
|
|
@@ -159,3 +170,6 @@ export * from "./ugc.js";
|
|
|
159
170
|
export * from "./ugc-amplification-identity.js";
|
|
160
171
|
export * from "./knowledge-repository.js";
|
|
161
172
|
export * from "./knowledge-bases.js";
|
|
173
|
+
export * from "./external-write-policy.js";
|
|
174
|
+
export * from "./log-sink-selector.js";
|
|
175
|
+
export * from "./otlp-log-sink.js";
|
|
@@ -36,15 +36,13 @@
|
|
|
36
36
|
* That difference is observable, so it is a choice the caller makes, not one
|
|
37
37
|
* this module makes for it.
|
|
38
38
|
*/
|
|
39
|
+
import { isRecord } from "./type-guards.js";
|
|
39
40
|
const NUMERIC_SEGMENT = /^\d+$/;
|
|
40
41
|
const TEMPLATE_SAFE_PATH = /^[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*$/;
|
|
41
42
|
// A head of one-or-more non-`[` characters, optionally followed by one or more
|
|
42
43
|
// `[123]` index groups. Anything else (a stray bracket, an unclosed group) is
|
|
43
44
|
// left as a single literal segment rather than silently reinterpreted.
|
|
44
45
|
const BRACKET_SEGMENT = /^([^[]+)((?:\[\d+\])+)?$/;
|
|
45
|
-
function isRecord(value) {
|
|
46
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47
|
-
}
|
|
48
46
|
/**
|
|
49
47
|
* A string that stores a serialized JSON object/array is treated as its parsed
|
|
50
48
|
* container; anything else is returned untouched — including strings that fail
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { parseDocument } from "yaml";
|
|
2
|
+
import { readTrimmedString as stringValue } from "./type-guards.js";
|
|
2
3
|
function record(value) {
|
|
3
4
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
4
5
|
? value
|
|
5
6
|
: null;
|
|
6
7
|
}
|
|
7
|
-
function stringValue(value) {
|
|
8
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
9
|
-
}
|
|
10
8
|
function strings(value) {
|
|
11
9
|
return Array.isArray(value)
|
|
12
10
|
? value
|
|
@@ -54,13 +54,13 @@
|
|
|
54
54
|
*/
|
|
55
55
|
import type { HostedAiLevel } from "./hosted-ai.js";
|
|
56
56
|
/** Hard ceiling on managed credits ONE workspace bootstrap may spend, all phases. */
|
|
57
|
-
export declare const KNOWLEDGE_BOOTSTRAP_MAX_CREDITS =
|
|
57
|
+
export declare const KNOWLEDGE_BOOTSTRAP_MAX_CREDITS = 30;
|
|
58
58
|
/**
|
|
59
59
|
* Tighter sub-ceiling on the provider/enrichment half (search + page fetch), i.e.
|
|
60
60
|
* the external-money phase. Reaching it degrades the pass to `partial` — synthesize
|
|
61
61
|
* from what was read — rather than failing it.
|
|
62
62
|
*/
|
|
63
|
-
export declare const KNOWLEDGE_BOOTSTRAP_ENRICHMENT_MAX_CREDITS =
|
|
63
|
+
export declare const KNOWLEDGE_BOOTSTRAP_ENRICHMENT_MAX_CREDITS = 6;
|
|
64
64
|
/**
|
|
65
65
|
* Reasoning tier the synthesis pass runs at. `medium` ("Oxygen Balanced") is the
|
|
66
66
|
* deliberate middle: `low` cannot hold a company profile's structure together, and
|
|
@@ -73,6 +73,19 @@ export declare const KNOWLEDGE_BOOTSTRAP_SYNTHESIS_TIER: HostedAiLevel;
|
|
|
73
73
|
* it is a quality/latency knob, and the credit ceilings above remain the money guard.
|
|
74
74
|
*/
|
|
75
75
|
export declare const KNOWLEDGE_BOOTSTRAP_MAX_PAGES_READ = 5;
|
|
76
|
+
/**
|
|
77
|
+
* Per-call provider abort budget for every paid leg of the pass (LinkedIn company,
|
|
78
|
+
* person search/profile, page search, page read), in milliseconds.
|
|
79
|
+
*
|
|
80
|
+
* The platform default is 120 s, sized for a durable column run. This pass runs
|
|
81
|
+
* inside the worker's tenant-maintenance tick under a 300 s step deadline, so two
|
|
82
|
+
* hung LinkedIn calls at the default (measured 2026-09-15/16: the managed vendor
|
|
83
|
+
* accepted connections and never answered) consumed the whole budget before the
|
|
84
|
+
* page reads and the synthesis got a turn. 45 s keeps the worst case — five legs,
|
|
85
|
+
* every one hanging — inside the step, and a healthy vendor answers in well under
|
|
86
|
+
* it. This bounds latency only; the credit ceilings above remain the money guard.
|
|
87
|
+
*/
|
|
88
|
+
export declare const KNOWLEDGE_BOOTSTRAP_PROVIDER_TIMEOUT_MS = 45000;
|
|
76
89
|
/** Fleet-wide kill switch, matching OXYGEN_KNOWLEDGE_RETRIEVAL/AUTO_SOURCES_DISABLED. */
|
|
77
90
|
export declare const KNOWLEDGE_BOOTSTRAP_DISABLED_ENV_VAR = "OXYGEN_KNOWLEDGE_BOOTSTRAP_DISABLED";
|
|
78
91
|
/**
|
|
@@ -172,6 +185,8 @@ export type KnowledgeBootstrapConsent = {
|
|
|
172
185
|
/** New workspace creation may authorize bounded creator-profile research. */
|
|
173
186
|
scope?: "company_and_creator";
|
|
174
187
|
domain?: string;
|
|
188
|
+
/** Canonical `/in/` profile supplied by this exact creator; never company evidence. */
|
|
189
|
+
creator_linkedin_url?: string;
|
|
175
190
|
/** ISO timestamp consent was recorded. Must parse; a marker that does not is not consent. */
|
|
176
191
|
granted_at: string;
|
|
177
192
|
/**
|
|
@@ -52,14 +52,15 @@
|
|
|
52
52
|
* Professional person evidence remains actor-scoped; ICP/offers are hypotheses.
|
|
53
53
|
* The provider sub-cap covers every external research leg, including fallbacks.
|
|
54
54
|
*/
|
|
55
|
+
import { normalizeLinkedinProfileUrl } from "./linkedin-url.js";
|
|
55
56
|
/** Hard ceiling on managed credits ONE workspace bootstrap may spend, all phases. */
|
|
56
|
-
export const KNOWLEDGE_BOOTSTRAP_MAX_CREDITS =
|
|
57
|
+
export const KNOWLEDGE_BOOTSTRAP_MAX_CREDITS = 30;
|
|
57
58
|
/**
|
|
58
59
|
* Tighter sub-ceiling on the provider/enrichment half (search + page fetch), i.e.
|
|
59
60
|
* the external-money phase. Reaching it degrades the pass to `partial` — synthesize
|
|
60
61
|
* from what was read — rather than failing it.
|
|
61
62
|
*/
|
|
62
|
-
export const KNOWLEDGE_BOOTSTRAP_ENRICHMENT_MAX_CREDITS =
|
|
63
|
+
export const KNOWLEDGE_BOOTSTRAP_ENRICHMENT_MAX_CREDITS = 6;
|
|
63
64
|
/**
|
|
64
65
|
* Reasoning tier the synthesis pass runs at. `medium` ("Oxygen Balanced") is the
|
|
65
66
|
* deliberate middle: `low` cannot hold a company profile's structure together, and
|
|
@@ -72,6 +73,19 @@ export const KNOWLEDGE_BOOTSTRAP_SYNTHESIS_TIER = "medium";
|
|
|
72
73
|
* it is a quality/latency knob, and the credit ceilings above remain the money guard.
|
|
73
74
|
*/
|
|
74
75
|
export const KNOWLEDGE_BOOTSTRAP_MAX_PAGES_READ = 5;
|
|
76
|
+
/**
|
|
77
|
+
* Per-call provider abort budget for every paid leg of the pass (LinkedIn company,
|
|
78
|
+
* person search/profile, page search, page read), in milliseconds.
|
|
79
|
+
*
|
|
80
|
+
* The platform default is 120 s, sized for a durable column run. This pass runs
|
|
81
|
+
* inside the worker's tenant-maintenance tick under a 300 s step deadline, so two
|
|
82
|
+
* hung LinkedIn calls at the default (measured 2026-09-15/16: the managed vendor
|
|
83
|
+
* accepted connections and never answered) consumed the whole budget before the
|
|
84
|
+
* page reads and the synthesis got a turn. 45 s keeps the worst case — five legs,
|
|
85
|
+
* every one hanging — inside the step, and a healthy vendor answers in well under
|
|
86
|
+
* it. This bounds latency only; the credit ceilings above remain the money guard.
|
|
87
|
+
*/
|
|
88
|
+
export const KNOWLEDGE_BOOTSTRAP_PROVIDER_TIMEOUT_MS = 45_000;
|
|
75
89
|
/** Fleet-wide kill switch, matching OXYGEN_KNOWLEDGE_RETRIEVAL/AUTO_SOURCES_DISABLED. */
|
|
76
90
|
export const KNOWLEDGE_BOOTSTRAP_DISABLED_ENV_VAR = "OXYGEN_KNOWLEDGE_BOOTSTRAP_DISABLED";
|
|
77
91
|
/**
|
|
@@ -158,14 +172,22 @@ export function readKnowledgeBootstrapConsent(metadata) {
|
|
|
158
172
|
return null;
|
|
159
173
|
if (!Number.isFinite(Date.parse(grantedAt)))
|
|
160
174
|
return null;
|
|
175
|
+
const scope = record.scope === "company_and_creator" ? "company_and_creator" : undefined;
|
|
176
|
+
const grantedBy = typeof record.granted_by_clerk_user_id === "string" && record.granted_by_clerk_user_id.length > 0
|
|
177
|
+
? record.granted_by_clerk_user_id
|
|
178
|
+
: null;
|
|
179
|
+
// A person hint is valid only as part of the creator-scoped grant. Otherwise a
|
|
180
|
+
// stale metadata value could be mistaken for an organization-wide profile hint.
|
|
181
|
+
const creatorLinkedinUrl = scope && grantedBy && typeof record.creator_linkedin_url === "string"
|
|
182
|
+
? normalizeLinkedinProfileUrl(record.creator_linkedin_url)?.dispatchUrl ?? null
|
|
183
|
+
: null;
|
|
161
184
|
return {
|
|
162
185
|
granted_at: grantedAt,
|
|
163
|
-
...(
|
|
186
|
+
...(scope ? { scope } : {}),
|
|
164
187
|
...(typeof record.domain === "string" ? { domain: record.domain } : {}),
|
|
188
|
+
...(creatorLinkedinUrl ? { creator_linkedin_url: creatorLinkedinUrl } : {}),
|
|
165
189
|
source: typeof record.source === "string" && record.source.length > 0 ? record.source : null,
|
|
166
|
-
granted_by_clerk_user_id:
|
|
167
|
-
? record.granted_by_clerk_user_id
|
|
168
|
-
: null,
|
|
190
|
+
granted_by_clerk_user_id: grantedBy,
|
|
169
191
|
};
|
|
170
192
|
}
|
|
171
193
|
/**
|