@oxygen-agent/cli 1.1011.405 → 1.1011.446

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +8 -4
  3. package/dist/connectors-commands.js +3 -0
  4. package/dist/contract-flags.js +22 -9
  5. package/dist/customer-operation-commands.d.ts +5 -0
  6. package/dist/customer-operation-commands.js +101 -0
  7. package/dist/database-query-flags.d.ts +2 -0
  8. package/dist/database-query-flags.js +6 -0
  9. package/dist/help.d.ts +3 -1
  10. package/dist/help.js +33 -0
  11. package/dist/http-client.d.ts +2 -0
  12. package/dist/http-client.js +41 -1
  13. package/dist/index.js +470 -174
  14. package/dist/knowledge-repository-commands.js +12 -14
  15. package/dist/mailbox-ramp-notice.d.ts +11 -0
  16. package/dist/mailbox-ramp-notice.js +53 -0
  17. package/dist/settings-commands.js +5 -4
  18. package/dist/skills.d.ts +5 -4
  19. package/dist/skills.js +106 -79
  20. package/dist/tool-catalog-detail-command.d.ts +7 -0
  21. package/dist/tool-catalog-detail-command.js +19 -0
  22. package/dist/tool-catalog-search-window.d.ts +6 -0
  23. package/dist/tool-catalog-search-window.js +12 -0
  24. package/dist/transcript.js +1 -0
  25. package/node_modules/@oxygen/shared/dist/bounded-read-result.d.ts +1 -1
  26. package/node_modules/@oxygen/shared/dist/bounded-read-result.js +2 -2
  27. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +17 -1
  28. package/node_modules/@oxygen/shared/dist/capability-discovery.js +359 -34
  29. package/node_modules/@oxygen/shared/dist/connection-approval.d.ts +2 -0
  30. package/node_modules/@oxygen/shared/dist/connection-approval.js +2 -0
  31. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.d.ts +2 -0
  32. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +110 -0
  33. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.d.ts +4 -0
  34. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.js +39 -0
  35. package/node_modules/@oxygen/shared/dist/contracts/crm-enrichment-read.d.ts +21 -0
  36. package/node_modules/@oxygen/shared/dist/contracts/crm-enrichment-read.js +15 -0
  37. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.d.ts +2 -0
  38. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +131 -0
  39. package/node_modules/@oxygen/shared/dist/contracts/database.d.ts +30 -0
  40. package/node_modules/@oxygen/shared/dist/contracts/database.js +29 -0
  41. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.d.ts +43 -0
  42. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.js +20 -0
  43. package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.d.ts +12 -0
  44. package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.js +8 -0
  45. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +48 -2
  46. package/node_modules/@oxygen/shared/dist/contracts/operations.js +67 -7
  47. package/node_modules/@oxygen/shared/dist/contracts/table-grid-query.d.ts +4 -0
  48. package/node_modules/@oxygen/shared/dist/contracts/table-grid-query.js +6 -0
  49. package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.d.ts +21 -0
  50. package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.js +27 -0
  51. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-detail.d.ts +19 -0
  52. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-detail.js +10 -0
  53. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-search-window.d.ts +12 -0
  54. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-search-window.js +7 -0
  55. package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.d.ts +1 -0
  56. package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.js +12 -0
  57. package/node_modules/@oxygen/shared/dist/email-campaign-ramp.d.ts +104 -0
  58. package/node_modules/@oxygen/shared/dist/email-campaign-ramp.js +250 -0
  59. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +1 -2
  60. package/node_modules/@oxygen/shared/dist/plan-limits.js +2 -2
  61. package/node_modules/@oxygen/shared/dist/provider-input-value.d.ts +5 -0
  62. package/node_modules/@oxygen/shared/dist/provider-input-value.js +10 -0
  63. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  64. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  65. package/node_modules/@oxygen/shared/package.json +3 -6
  66. package/package.json +1 -1
  67. package/node_modules/@oxygen/shared/dist/copilot-skills.d.ts +0 -15
  68. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +0 -31
  69. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +0 -41
  70. package/node_modules/@oxygen/shared/dist/copilot-skills.js +0 -6
@@ -0,0 +1,8 @@
1
+ /** Exact selectors for one native/provider sent-invitation page. */
2
+ import { int, obj, str } from "./schema.js";
3
+ export const LINKEDIN_SENT_INVITES_MAX_LIMIT = 200;
4
+ export const LINKEDIN_SENT_INVITES_QUERY_CONTRACT = obj({
5
+ account: str({ description: "Sender id, connection id, or Unipile account id." }),
6
+ limit: int({ minimum: 1, description: "Maximum invitations to return." }),
7
+ cursor: str({ description: "Pagination cursor from a previous response." }),
8
+ });
@@ -1,13 +1,41 @@
1
- import type { ContractObject } from "./schema.js";
1
+ import { type ContractObject } from "./schema.js";
2
2
  export type OperationSpec<C extends ContractObject = ContractObject> = {
3
3
  /** Stable id, `<resource>.<verb>`. */
4
4
  id: string;
5
5
  /** Route template; `{name}` segments are path parameters. */
6
6
  route: string;
7
- method: "POST" | "PATCH";
7
+ method: "GET" | "POST" | "PATCH" | "DELETE";
8
8
  /** When set, the contract describes `body[bodyKey]` rather than the whole body. */
9
9
  bodyKey?: string;
10
10
  contract: C;
11
+ /** Named route parameters, exposed as exact flags/properties by generated surfaces. */
12
+ parameters?: readonly string[];
13
+ /** Fields sent in the query rather than the JSON body (all fields for GET). */
14
+ queryFields?: readonly string[];
15
+ /** Array fields encoded as repeated query keys rather than a JSON string. */
16
+ repeatedQueryFields?: readonly string[];
17
+ /** Fixed discriminator fields owned by this operation, never supplied by a caller. */
18
+ defaults?: Readonly<Record<string, unknown>>;
19
+ /** A side already has a manual, domain-native implementation. */
20
+ generated?: {
21
+ cli: boolean;
22
+ mcp: boolean;
23
+ };
24
+ /** Multipart transport for an operation whose API takes one uploaded file. */
25
+ multipart?: {
26
+ contentField: string;
27
+ nameField: string;
28
+ typeField: string;
29
+ fileField: string;
30
+ };
31
+ /** Download bytes through the same authenticated route; never follow redirects. */
32
+ binary?: {
33
+ maxBytes: number;
34
+ stream?: never;
35
+ } | {
36
+ stream: true;
37
+ resourceParameter: string;
38
+ };
11
39
  cli: {
12
40
  /** Command words after `oxygen`, e.g. `["senders", "limits", "set"]`. */
13
41
  path: readonly string[];
@@ -24,10 +52,28 @@ export type OperationSpec<C extends ContractObject = ContractObject> = {
24
52
  description: string;
25
53
  /** The MCP toolset domain (pack) the tool registers under. */
26
54
  pack: string;
55
+ /** Source-reviewed effect hints; never an authorization bypass. */
56
+ annotations?: {
57
+ destructiveHint?: boolean;
58
+ openWorldHint?: boolean;
59
+ idempotentHint?: boolean;
60
+ };
27
61
  };
28
62
  mutates: boolean;
29
63
  spends: boolean;
30
64
  };
31
65
  /** The route with its `{param}` segments filled in, each URL-encoded. */
32
66
  export declare function operationPath(spec: Pick<OperationSpec, "route">, params: Readonly<Record<string, string>>): string;
67
+ /** The exact public grammar, including the operation's scoped route parameters. */
68
+ export declare function operationInputContract(spec: OperationSpec): ContractObject;
69
+ /** Preserve absent, false, null and empty values while assigning each field to its transport. */
70
+ export declare function operationRequest(spec: OperationSpec, args: Record<string, unknown>): {
71
+ path: string;
72
+ method: OperationSpec["method"];
73
+ body?: Record<string, unknown>;
74
+ };
75
+ /** Decode the explicit file property and keep the native multipart API contract. */
76
+ export declare function operationFormData(spec: OperationSpec, body: Record<string, unknown>): FormData;
33
77
  export declare const OPERATION_SPECS: readonly OperationSpec[];
78
+ export declare const CLI_OPERATIONS: OperationSpec[];
79
+ export declare const MCP_OPERATIONS: OperationSpec[];
@@ -5,8 +5,11 @@
5
5
  * cannot disagree on which fields exist. Leaf and browser-safe.
6
6
  */
7
7
  import { CONNECTOR_EVENTS_REVOKE_OPERATION, CONNECTOR_UPDATE_OPERATION } from "./connectors.js";
8
+ import { str } from "./schema.js";
8
9
  import { SENDER_LIMITS_SET_OPERATION } from "./sender-limits.js";
9
- import { WORKSPACE_PAGES_UPDATE_OPERATION } from "./workspace-pages.js";
10
+ import { WORKSPACE_PAGES_GET_OPERATION, WORKSPACE_PAGES_UPDATE_OPERATION } from "./workspace-pages.js";
11
+ import { BROWSER_OPERATIONS } from "./browser-operations.js";
12
+ import { CUSTOMER_OPERATIONS } from "./customer-operations.js";
10
13
  /** The route with its `{param}` segments filled in, each URL-encoded. */
11
14
  export function operationPath(spec, params) {
12
15
  return spec.route.replace(/\{(\w+)\}/g, (_match, name) => {
@@ -16,9 +19,66 @@ export function operationPath(spec, params) {
16
19
  return encodeURIComponent(value);
17
20
  });
18
21
  }
19
- export const OPERATION_SPECS = [
20
- SENDER_LIMITS_SET_OPERATION,
21
- CONNECTOR_UPDATE_OPERATION,
22
- CONNECTOR_EVENTS_REVOKE_OPERATION,
23
- WORKSPACE_PAGES_UPDATE_OPERATION,
24
- ];
22
+ /** The exact public grammar, including the operation's scoped route parameters. */
23
+ export function operationInputContract(spec) {
24
+ const parameters = spec.parameters ?? [];
25
+ for (const name of parameters) {
26
+ if (Object.hasOwn(spec.contract.properties, name))
27
+ throw new Error(`Operation ${spec.id} uses ${name} for both a route parameter and a body field.`);
28
+ }
29
+ return {
30
+ ...spec.contract,
31
+ properties: {
32
+ ...Object.fromEntries(parameters.map((name) => [name, str({ minLength: 1 })])),
33
+ ...spec.contract.properties,
34
+ },
35
+ required: [...parameters, ...(spec.contract.required ?? [])],
36
+ };
37
+ }
38
+ /** Preserve absent, false, null and empty values while assigning each field to its transport. */
39
+ export function operationRequest(spec, args) {
40
+ const params = Object.fromEntries((spec.parameters ?? []).map((name) => [name, String(args[name] ?? "")]));
41
+ let path = operationPath(spec, params);
42
+ const body = { ...spec.defaults };
43
+ const query = new URLSearchParams();
44
+ for (const name of Object.keys(spec.contract.properties)) {
45
+ const value = args[name];
46
+ if (value === undefined)
47
+ continue;
48
+ if (spec.method === "GET" || spec.queryFields?.includes(name)) {
49
+ if (spec.repeatedQueryFields?.includes(name) && Array.isArray(value)) {
50
+ for (const entry of value)
51
+ query.append(name, String(entry));
52
+ }
53
+ else if (value !== null)
54
+ query.set(name, typeof value === "object" ? JSON.stringify(value) : String(value));
55
+ }
56
+ else
57
+ body[name] = value;
58
+ }
59
+ if (query.size)
60
+ path += `?${query}`;
61
+ return { path, method: spec.method, ...(spec.method !== "GET" && Object.keys(body).length > 0 ? { body: spec.bodyKey ? { [spec.bodyKey]: body } : body } : {}) };
62
+ }
63
+ /** Decode the explicit file property and keep the native multipart API contract. */
64
+ export function operationFormData(spec, body) {
65
+ const fields = spec.multipart;
66
+ if (!fields)
67
+ throw new Error("This operation does not accept a file.");
68
+ const encoded = body[fields.contentField];
69
+ if (typeof encoded !== "string" || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(encoded) || !encoded) {
70
+ throw new Error(`${fields.contentField} must contain valid non-empty base64 file bytes.`);
71
+ }
72
+ const bytes = Uint8Array.from(atob(encoded), (character) => character.charCodeAt(0));
73
+ const form = new FormData();
74
+ form.set(fields.fileField, new Blob([bytes], { type: String(body[fields.typeField] ?? "application/octet-stream") }), String(body[fields.nameField] ?? "upload"));
75
+ for (const [key, value] of Object.entries(body)) {
76
+ if ([fields.contentField, fields.nameField, fields.typeField].includes(key) || value === undefined)
77
+ continue;
78
+ form.set(key, typeof value === "object" ? JSON.stringify(value) : String(value));
79
+ }
80
+ return form;
81
+ }
82
+ export const OPERATION_SPECS = [SENDER_LIMITS_SET_OPERATION, CONNECTOR_UPDATE_OPERATION, CONNECTOR_EVENTS_REVOKE_OPERATION, WORKSPACE_PAGES_GET_OPERATION, WORKSPACE_PAGES_UPDATE_OPERATION, ...CUSTOMER_OPERATIONS, ...BROWSER_OPERATIONS];
83
+ export const CLI_OPERATIONS = [...CUSTOMER_OPERATIONS, ...BROWSER_OPERATIONS].filter((spec) => spec.generated?.cli !== false);
84
+ export const MCP_OPERATIONS = [...CUSTOMER_OPERATIONS, ...BROWSER_OPERATIONS].filter((spec) => spec.generated?.mcp !== false);
@@ -0,0 +1,4 @@
1
+ export declare const TABLE_GRID_QUERY_OPTIONS_CONTRACT: import("./schema.js").ContractObject<{
2
+ readonly include_stats: import("./schema.js").BooleanProperty;
3
+ readonly include_fill: import("./schema.js").BooleanProperty;
4
+ }, never>;
@@ -0,0 +1,6 @@
1
+ /** Optional first-page metadata on the shared Table grid query. */
2
+ import { bool, obj } from "./schema.js";
3
+ export const TABLE_GRID_QUERY_OPTIONS_CONTRACT = obj({
4
+ include_stats: bool({ description: "With --format grid, include column fill statistics and their table row count when available; 0 credits." }),
5
+ include_fill: bool({ description: "With --format grid, include fill progress on an empty first page when available; ignored on cursor pages. 0 credits." }),
6
+ });
@@ -0,0 +1,21 @@
1
+ /** Optional representation of Query; canonical raw rows remain the default. */
2
+ export declare const TABLE_QUERY_GRID_CONTRACT: import("./schema.js").ContractObject<{
3
+ readonly format: {
4
+ type: "string";
5
+ enum: readonly "grid"[];
6
+ description?: string;
7
+ };
8
+ readonly include_stats: import("./schema.js").BooleanProperty;
9
+ readonly include_fill: import("./schema.js").BooleanProperty;
10
+ readonly max_chars: {
11
+ type: "integer";
12
+ description?: string;
13
+ minimum?: number;
14
+ maximum?: number;
15
+ "x-clamp"?: true;
16
+ };
17
+ }, never>;
18
+ export declare const TABLE_QUERY_GRID_NATIVE_WINDOW = 10000;
19
+ export declare const TABLE_QUERY_GRID_BUILTIN_WINDOW = 4000;
20
+ /** Measure the complete native receipt and the escaped built-in host receipt. */
21
+ export declare function tableQueryGridResultFits(data: Record<string, unknown>, maxChars: number | undefined, surface: string | null): boolean;
@@ -0,0 +1,27 @@
1
+ import { success } from "../cli-result.js";
2
+ import { OXYGEN_VERSION } from "../version.js";
3
+ import { bool, enumOf, int, obj } from "./schema.js";
4
+ /** Optional representation of Query; canonical raw rows remain the default. */
5
+ export const TABLE_QUERY_GRID_CONTRACT = obj({
6
+ format: enumOf(["grid"], { description: "Grid cell previews." }),
7
+ include_stats: bool({ description: "Optional column stats." }),
8
+ include_fill: bool({ description: "Empty first-page fill state." }),
9
+ max_chars: int({ minimum: 1_000, maximum: 100_000, description: "Complete grid envelope chars." }),
10
+ });
11
+ export const TABLE_QUERY_GRID_NATIVE_WINDOW = 10_000;
12
+ // Deliberately below Strands' shipped 6000 default; counts the complete host
13
+ // object rather than only its text. This changes no adapter's outer ceiling.
14
+ export const TABLE_QUERY_GRID_BUILTIN_WINDOW = 4_000;
15
+ /** Measure the complete native receipt and the escaped built-in host receipt. */
16
+ export function tableQueryGridResultFits(data, maxChars, surface) {
17
+ if (maxChars === undefined)
18
+ return true;
19
+ if (JSON.stringify(success("tables query", data)).length > maxChars)
20
+ return false;
21
+ if (surface === "agent" || surface === "copilot") {
22
+ return JSON.stringify({
23
+ ok: true, text: JSON.stringify(data), structured: { ...data, oxygen_version: OXYGEN_VERSION },
24
+ }).length <= TABLE_QUERY_GRID_BUILTIN_WINDOW;
25
+ }
26
+ return true;
27
+ }
@@ -0,0 +1,19 @@
1
+ import { type Infer } from "./schema.js";
2
+ /** Explicit metadata projections; omitted native HTTP/CLI options retain full detail. */
3
+ export declare const toolCatalogDetailQueryContract: import("./schema.js").ContractObject<{
4
+ readonly view: {
5
+ type: "string";
6
+ enum: readonly ("access" | "examples" | "field_names" | "full" | "guidance" | "input_schema" | "native_fields" | "output_schema" | "pricing" | "summary")[];
7
+ description?: string;
8
+ };
9
+ readonly field_keys: import("./schema.js").ArrayProperty<import("./schema.js").StringProperty>;
10
+ readonly field_query: import("./schema.js").StringProperty;
11
+ readonly max_chars: {
12
+ type: "integer";
13
+ description?: string;
14
+ minimum?: number;
15
+ maximum?: number;
16
+ "x-clamp"?: true;
17
+ };
18
+ }, never>;
19
+ export type ToolCatalogDetailQuery = Infer<typeof toolCatalogDetailQueryContract>;
@@ -0,0 +1,10 @@
1
+ import { arr, enumOf, int, obj, str } from "./schema.js";
2
+ /** Explicit metadata projections; omitted native HTTP/CLI options retain full detail. */
3
+ export const toolCatalogDetailQueryContract = obj({
4
+ view: enumOf(["summary", "pricing", "access", "guidance", "field_names", "native_fields", "examples", "input_schema", "output_schema", "full"], {
5
+ description: "Summary defaults on MCP; full defaults on HTTP/CLI. Schema views stay exact.",
6
+ }),
7
+ field_keys: arr(str({ minLength: 1, maxLength: 200 }), { minItems: 1, maxItems: 20, description: "Exact native field keys; field_names/native_fields only." }),
8
+ field_query: str({ minLength: 1, maxLength: 200, description: "Substring of native field keys; not with field_keys." }),
9
+ max_chars: int({ minimum: 2_000, maximum: 40_000, description: "Native receipt window; the calling surface may enforce a lower cap." }),
10
+ });
@@ -0,0 +1,12 @@
1
+ /** Existing native filters plus an optional window; omission retains full HTTP/CLI defaults. */
2
+ export declare const toolCatalogSearchWindowContract: import("./schema.js").ContractObject<{
3
+ readonly max_chars: {
4
+ type: "integer";
5
+ description?: string;
6
+ minimum?: number;
7
+ maximum?: number;
8
+ "x-clamp"?: true;
9
+ };
10
+ readonly include_native: import("./schema.js").BooleanProperty;
11
+ readonly workflow_eligible: import("./schema.js").BooleanProperty;
12
+ }, never>;
@@ -0,0 +1,7 @@
1
+ import { bool, int, obj } from "./schema.js";
2
+ /** Existing native filters plus an optional window; omission retains full HTTP/CLI defaults. */
3
+ export const toolCatalogSearchWindowContract = obj({
4
+ max_chars: int({ minimum: 2_000, maximum: 40_000, description: "Native window; surface cap applies." }),
5
+ include_native: bool({ description: "Include native column metadata (default true)." }),
6
+ workflow_eligible: bool({ description: "Only hosted-workflow tools; includes canonical workflow_effect." }),
7
+ });
@@ -13,4 +13,5 @@ export declare const WORKSPACE_PAGES_UPDATE_CONTRACT: import("./schema.js").Cont
13
13
  readonly show_all: import("./schema.js").BooleanProperty;
14
14
  }, never>;
15
15
  export type WorkspacePagesUpdate = Infer<typeof WORKSPACE_PAGES_UPDATE_CONTRACT>;
16
+ export declare const WORKSPACE_PAGES_GET_OPERATION: OperationSpec;
16
17
  export declare const WORKSPACE_PAGES_UPDATE_OPERATION: OperationSpec<typeof WORKSPACE_PAGES_UPDATE_CONTRACT>;
@@ -17,11 +17,23 @@ export const WORKSPACE_PAGES_UPDATE_CONTRACT = obj({
17
17
  });
18
18
  const inferMatchesRoute = true;
19
19
  void inferMatchesRoute;
20
+ export const WORKSPACE_PAGES_GET_OPERATION = {
21
+ id: "workspace_pages.get",
22
+ route: "/api/cli/settings/pages",
23
+ method: "GET",
24
+ contract: obj({}),
25
+ generated: { cli: false, mcp: false },
26
+ cli: { path: ["settings", "pages", "list"], summary: "Page visibility in the web app's sidebar navigation menu (Settings → Customise): hidden_pages, whether you may customize it (can_change: admins and owners), then every page (path, name, section, parent) with hidden (hidden itself) and visible (shown in the sidebar; false under a hidden section). Read-only, 0 credits." },
27
+ mcp: { name: "oxygen_settings_pages_get", title: "Oxygen: List Sidebar Pages", description: "Lists the OXYGEN web app's sidebar navigation pages (Settings → Customise) with whether this workspace hides each one for everyone, and can_change (admins and owners). Free. Change with oxygen_settings_pages_update.", pack: "workspace" },
28
+ mutates: false,
29
+ spends: false,
30
+ };
20
31
  export const WORKSPACE_PAGES_UPDATE_OPERATION = {
21
32
  id: "workspace_pages.update",
22
33
  route: "/api/cli/settings/pages",
23
34
  method: "POST",
24
35
  contract: WORKSPACE_PAGES_UPDATE_CONTRACT,
36
+ generated: { cli: false, mcp: false },
25
37
  cli: {
26
38
  path: ["settings", "pages", "set"],
27
39
  summary: "Hide or show pages in the web app's sidebar navigation menu for everyone in this workspace (Settings → Customise): --hide Unibox,Publishing, --show Unibox, --show-all. Name pages by path or name from `settings pages list`; hiding a section hides its sub-pages. Nothing is deleted and hidden pages stay reachable by URL. Admins and owners only; free.",
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The standard ramp, by completed sending days: 5 a day through 2, 10 through 5,
3
+ * 20 through 8, then the configured cap. `maxAgeDays` keeps its historical name;
4
+ * its input is completed sending days, not mailbox age.
5
+ */
6
+ export declare const WARMUP_RAMP_DAILY_CAPS: readonly [{
7
+ readonly maxAgeDays: 2;
8
+ readonly cap: 5;
9
+ }, {
10
+ readonly maxAgeDays: 5;
11
+ readonly cap: 10;
12
+ }, {
13
+ readonly maxAgeDays: 8;
14
+ readonly cap: 20;
15
+ }];
16
+ /**
17
+ * A custom ramp. `cap`, when present, is a permanent ceiling for this inbox (it
18
+ * never climbs past it, even when the daily cap is higher); absent, the ramp
19
+ * climbs until it reaches the inbox's configured daily cap.
20
+ */
21
+ export type MailboxCustomRampConfig = {
22
+ start_per_day: number;
23
+ increase_every_days: number;
24
+ step: number;
25
+ cap?: number;
26
+ };
27
+ /** The ramp switched off: the configured daily cap applies from the first sending day. */
28
+ export type MailboxRampOffConfig = {
29
+ enabled: false;
30
+ };
31
+ export type MailboxRampConfig = MailboxCustomRampConfig | MailboxRampOffConfig;
32
+ export declare const MAILBOX_RAMP_MODES: readonly ["standard", "custom", "off"];
33
+ export type MailboxRampMode = (typeof MAILBOX_RAMP_MODES)[number];
34
+ export declare function isMailboxRampOff(config: MailboxRampConfig | null | undefined): config is MailboxRampOffConfig;
35
+ export declare function mailboxRampMode(config: MailboxRampConfig | null | undefined): MailboxRampMode;
36
+ /** The custom knobs, or null for the standard ramp and for a ramp switched off. */
37
+ export declare function customMailboxRamp(config: MailboxRampConfig | null | undefined): MailboxCustomRampConfig | null;
38
+ export type MailboxRampConfigParse = {
39
+ ok: true;
40
+ config: MailboxRampConfig | null;
41
+ } | {
42
+ ok: false;
43
+ field: string | null;
44
+ message: string;
45
+ };
46
+ /**
47
+ * The one validator for a ramp_config write, shared by the API route (400) and
48
+ * the tenant write (OxygenError). null/undefined selects the standard ramp.
49
+ * `{enabled: false}` turns the ramp off and takes no other field. Otherwise
50
+ * start_per_day, increase_every_days (>= 1) and step are required non-negative
51
+ * integers, and cap is an optional non-negative integer.
52
+ */
53
+ export declare function parseMailboxRampConfig(value: unknown): MailboxRampConfigParse;
54
+ /**
55
+ * Encode a validated public config for rolling web/worker deployments. The
56
+ * snapshot is the configured cap in force, taken under the mailbox's row lock.
57
+ * New readers discard it; it is never a permanent custom ceiling. Cap edits
58
+ * must refresh it, including Off's fixed start, so old warm-up targets cannot
59
+ * retain a higher value after the mailbox's daily cap is lowered.
60
+ */
61
+ export declare function serializeMailboxRampConfig(config: MailboxRampConfig | null, configuredDailyCap: number): Record<string, unknown> | null;
62
+ /** Non-throwing read of stored ramp_config; private compatibility fields never reach public readbacks. */
63
+ export declare function readMailboxRampConfig(value: unknown): MailboxRampConfig | null;
64
+ /** Refresh only a synthetic compatibility ceiling; explicit caps and malformed legacy rows stay intact. */
65
+ export declare function refreshMailboxRampConfigDailyCap(value: unknown, configuredDailyCap: number): unknown;
66
+ /** The standard ramp's ceiling after `sendingDays` completed sending days; null once past its tiers. */
67
+ export declare function warmupRampDailyCap(sendingDays: number): number | null;
68
+ /**
69
+ * A custom ramp's ceiling after `sendingDays` completed sending days: start_per_day
70
+ * plus one `step` per completed `increase_every_days` window, held to `cap` when
71
+ * one is set. A (defensively) zero interval never climbs instead of dividing by zero.
72
+ */
73
+ export declare function rampConfigDailyCap(config: MailboxCustomRampConfig, sendingDays: number): number;
74
+ /** The campaign ramp's ceiling in any mode; null when the ramp does not hold the inbox back. */
75
+ export declare function campaignRampDailyCap(config: MailboxRampConfig | null | undefined, sendingDays: number): number | null;
76
+ /** One level of the schedule: from this 1-based sending day the inbox may send `perDay`. */
77
+ export type CampaignRampStep = {
78
+ fromSendingDay: number;
79
+ perDay: number;
80
+ };
81
+ export type CampaignRampSchedule = {
82
+ mode: MailboxRampMode;
83
+ /** Every level the inbox passes through, ending at the most it will ever send a day. */
84
+ steps: CampaignRampStep[];
85
+ /** The 1-based sending day on which the inbox first sends its full daily cap; null if it never does. */
86
+ fullCapOnSendingDay: number | null;
87
+ /** True when `steps` stopped early at `maxSteps`. */
88
+ truncated: boolean;
89
+ };
90
+ /**
91
+ * The 1-based sending day on which the ramp first lets the inbox send its full
92
+ * daily cap: 1 when the ramp is off or already at the cap, null when it never
93
+ * gets there (a custom cap below the daily cap, or no increase).
94
+ */
95
+ export declare function campaignRampFullCapSendingDay(config: MailboxRampConfig | null | undefined, dailyCap: number): number | null;
96
+ /**
97
+ * The day-by-day plan a ramp produces against a daily cap, as distinct levels.
98
+ * Standard against 25: 5 (day 1), 10 (day 4), 20 (day 7), 25 (day 10). A custom
99
+ * ramp whose own cap sits below the daily cap ends at that cap and never reports
100
+ * a full-cap day. `maxSteps` bounds a very gentle custom ramp (1 a day, +1 a day).
101
+ */
102
+ export declare function campaignRampSchedule(config: MailboxRampConfig | null | undefined, dailyCap: number, options?: {
103
+ maxSteps?: number;
104
+ }): CampaignRampSchedule;