@oxygen-agent/cli 1.1014.777 → 1.1014.913

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 (64) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +10 -1
  3. package/dist/commands/admin.js +1 -1
  4. package/dist/commands/billing.js +13 -9
  5. package/dist/commands/columns.js +3 -3
  6. package/dist/commands/crm.js +2 -2
  7. package/dist/commands/deliverability.js +18 -10
  8. package/dist/commands/domains.js +15 -3
  9. package/dist/commands/mailboxes.js +22 -20
  10. package/dist/commands/managed-inboxes.js +1 -1
  11. package/dist/commands/sequences.js +34 -8
  12. package/dist/commands/voice.d.ts +21 -0
  13. package/dist/commands/voice.js +121 -6
  14. package/dist/customer-operation-commands.js +8 -2
  15. package/dist/index.js +6 -0
  16. package/dist/run-wait.js +10 -2
  17. package/node_modules/@oxygen/formula/package.json +8 -0
  18. package/node_modules/@oxygen/shared/dist/agent-sessions.d.ts +3 -0
  19. package/node_modules/@oxygen/shared/dist/agent-sessions.js +22 -1
  20. package/node_modules/@oxygen/shared/dist/billing-money.d.ts +31 -0
  21. package/node_modules/@oxygen/shared/dist/billing-money.js +60 -0
  22. package/node_modules/@oxygen/shared/dist/billing.d.ts +3 -0
  23. package/node_modules/@oxygen/shared/dist/billing.js +9 -1
  24. package/node_modules/@oxygen/shared/dist/capability-discovery.js +2 -2
  25. package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.d.ts +76 -0
  26. package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.js +4 -2
  27. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +2 -2
  28. package/node_modules/@oxygen/shared/dist/contracts/guided-activation.d.ts +177 -0
  29. package/node_modules/@oxygen/shared/dist/contracts/guided-activation.js +59 -0
  30. package/node_modules/@oxygen/shared/dist/contracts/operations.js +4 -3
  31. package/node_modules/@oxygen/shared/dist/copilot-access.js +2 -1
  32. package/node_modules/@oxygen/shared/dist/credit-guidance.js +8 -15
  33. package/node_modules/@oxygen/shared/dist/domain-authentication.d.ts +65 -0
  34. package/node_modules/@oxygen/shared/dist/domain-authentication.js +54 -0
  35. package/node_modules/@oxygen/shared/dist/email-infrastructure-policy.d.ts +5 -0
  36. package/node_modules/@oxygen/shared/dist/email-infrastructure-policy.js +13 -0
  37. package/node_modules/@oxygen/shared/dist/guided-activation.d.ts +155 -0
  38. package/node_modules/@oxygen/shared/dist/guided-activation.fixture.d.ts +15 -0
  39. package/node_modules/@oxygen/shared/dist/guided-activation.fixture.js +21 -0
  40. package/node_modules/@oxygen/shared/dist/guided-activation.js +38 -0
  41. package/node_modules/@oxygen/shared/dist/linkedin-connection-identity.d.ts +9 -0
  42. package/node_modules/@oxygen/shared/dist/linkedin-connection-identity.js +41 -0
  43. package/node_modules/@oxygen/shared/dist/managed-email-branding.js +9 -5
  44. package/node_modules/@oxygen/shared/dist/otlp-log-sink.d.ts +1 -0
  45. package/node_modules/@oxygen/shared/dist/otlp-log-sink.js +58 -54
  46. package/node_modules/@oxygen/shared/dist/placement-history.d.ts +3 -0
  47. package/node_modules/@oxygen/shared/dist/placement-history.js +12 -0
  48. package/node_modules/@oxygen/shared/dist/placement-report.d.ts +7 -0
  49. package/node_modules/@oxygen/shared/dist/placement-report.js +34 -0
  50. package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +1 -0
  51. package/node_modules/@oxygen/shared/dist/plan-limits-view.js +1 -0
  52. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +1 -0
  53. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +1 -0
  54. package/node_modules/@oxygen/shared/dist/sending-limits.d.ts +1 -0
  55. package/node_modules/@oxygen/shared/dist/sending-limits.js +2 -10
  56. package/node_modules/@oxygen/shared/dist/teammates.js +2 -1
  57. package/node_modules/@oxygen/shared/dist/user-capability-routing.js +87 -2
  58. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  59. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  60. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  61. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  62. package/node_modules/@oxygen/shared/dist/voice-countries.d.ts +66 -0
  63. package/node_modules/@oxygen/shared/dist/voice-countries.js +75 -0
  64. package/package.json +1 -1
@@ -75,9 +75,85 @@ export declare const AGENT_SESSION_CONFIG_CONTRACT: import("./schema.js").Contra
75
75
  };
76
76
  }, "daily_credits" | "per_run_credits">;
77
77
  readonly ends_at: import("./schema.js").StringProperty;
78
+ readonly guided_activation: import("./schema.js").ContractObject<{
79
+ readonly activation_id: import("./schema.js").StringProperty;
80
+ readonly purpose: {
81
+ type: "string";
82
+ enum: readonly ("lead_scan" | "morning_briefing")[];
83
+ description?: string;
84
+ };
85
+ readonly scan_policy: import("./schema.js").ContractObject<{
86
+ readonly version: {
87
+ type: "integer";
88
+ description?: string;
89
+ minimum?: number;
90
+ maximum?: number;
91
+ "x-clamp"?: true;
92
+ };
93
+ readonly table_id: import("./schema.js").StringProperty;
94
+ readonly chat_id: import("./schema.js").StringProperty;
95
+ readonly policy_digest: import("./schema.js").StringProperty;
96
+ readonly revisions: import("./schema.js").ContractObject<{
97
+ [k: string]: import("./schema.js").StringProperty;
98
+ }, "audience" | "authority" | "configuration" | "copy" | "knowledge" | "recipients" | "schedule" | "sender" | "sequence">;
99
+ readonly sources: import("./schema.js").ArrayProperty<import("./schema.js").ContractObject<{
100
+ readonly source_id: import("./schema.js").StringProperty;
101
+ readonly config_digest: import("./schema.js").StringProperty;
102
+ readonly incremental_mode: {
103
+ type: "string";
104
+ enum: readonly ("cursor" | "watermark")[];
105
+ description?: string;
106
+ };
107
+ readonly person_key_field: import("./schema.js").StringProperty;
108
+ }, "config_digest" | "incremental_mode" | "person_key_field" | "source_id">>;
109
+ readonly exclude_dnc: import("./schema.js").BooleanProperty;
110
+ readonly exclude_contacted: import("./schema.js").BooleanProperty;
111
+ readonly review_only: import("./schema.js").BooleanProperty;
112
+ readonly max_examined: {
113
+ type: "integer";
114
+ description?: string;
115
+ minimum?: number;
116
+ maximum?: number;
117
+ "x-clamp"?: true;
118
+ };
119
+ readonly max_new_leads: {
120
+ type: "integer";
121
+ description?: string;
122
+ minimum?: number;
123
+ maximum?: number;
124
+ "x-clamp"?: true;
125
+ };
126
+ readonly max_provider_pages: {
127
+ type: "integer";
128
+ description?: string;
129
+ minimum?: number;
130
+ maximum?: number;
131
+ "x-clamp"?: true;
132
+ };
133
+ readonly max_provider_credits: {
134
+ type: "number";
135
+ description?: string;
136
+ minimum?: number;
137
+ maximum?: number;
138
+ "x-clamp"?: true;
139
+ };
140
+ readonly max_inference_credits: {
141
+ type: "number";
142
+ description?: string;
143
+ minimum?: number;
144
+ maximum?: number;
145
+ "x-clamp"?: true;
146
+ };
147
+ }, "chat_id" | "exclude_contacted" | "exclude_dnc" | "max_examined" | "max_inference_credits" | "max_new_leads" | "max_provider_credits" | "max_provider_pages" | "policy_digest" | "review_only" | "revisions" | "sources" | "table_id" | "version">;
148
+ }, "activation_id" | "purpose">;
78
149
  readonly teammate_routine: import("./schema.js").ContractObject<{
79
150
  readonly chat_id: import("./schema.js").StringProperty;
80
151
  readonly proposal_id: import("./schema.js").StringProperty;
152
+ readonly report_policy: {
153
+ type: "string";
154
+ enum: readonly ("changes_only" | "periodic")[];
155
+ description?: string;
156
+ };
81
157
  }, "chat_id">;
82
158
  }, "actions" | "budget" | "events" | "goal" | "resources" | "schedule" | "template" | "title">;
83
159
  export declare const AGENT_SESSION_OPERATIONS: readonly OperationSpec[];
@@ -1,4 +1,5 @@
1
1
  import { arr, bool, enumOf, int, num, obj, str } from "./schema.js";
2
+ import { GUIDED_ACTIVATION_SCAN_POLICY_CONTRACT } from "./guided-activation.js";
2
3
  const strings = () => arr(str({ minLength: 1 }), { maxItems: 100 });
3
4
  export const AGENT_SESSION_CONFIG_CONTRACT = obj({
4
5
  title: str({ minLength: 1, maxLength: 160 }), goal: str({ minLength: 1, maxLength: 16000 }),
@@ -8,7 +9,8 @@ export const AGENT_SESSION_CONFIG_CONTRACT = obj({
8
9
  schedule: obj({ kind: enumOf(["interval", "cron"], { description: "interval (default) wakes every interval_minutes; cron wakes at each cron time in timezone and needs no interval fields." }), interval_minutes: int({ minimum: 5, description: "Default cadence in minutes; 1440 means one day." }), min_interval_minutes: int({ minimum: 5, description: "Earliest permitted interval when the Agent requests its next check." }), max_interval_minutes: int({ minimum: 5, maximum: 525600, description: "Latest permitted interval when the Agent requests its next check." }), cron: str({ minLength: 9, maxLength: 120, description: "Five fields, e.g. \"0 9 * * 1-5\"." }), timezone: str({ minLength: 1, maxLength: 64, description: "IANA zone for cron, e.g. Europe/Berlin." }) }),
9
10
  budget: obj({ per_run_credits: num({ minimum: 0.01, maximum: 25000, description: "Credit cap for each check, including inference and paid tools. The host reserves this full cap before starting." }), daily_credits: num({ minimum: 0.01, description: "Rolling 24-hour credit cap. Settled costs count for 24 hours; unresolved costs remain held until settled." }), lifetime_credits: num({ minimum: 0.01, description: "Optional total credit cap across all checks in this session." }) }, { required: ["per_run_credits", "daily_credits"] }),
10
11
  ends_at: str({ description: "Optional ISO end time." }),
11
- teammate_routine: obj({ chat_id: str({ minLength: 36, maxLength: 36 }), proposal_id: str({ minLength: 1, maxLength: 64 }) }, { required: ["chat_id"], description: "An Oxygent routine: your chat with this Agent, which its reports post into. Only you may then start, change or message it." }),
12
+ guided_activation: obj({ activation_id: str({ minLength: 36, maxLength: 36 }), purpose: enumOf(["lead_scan", "morning_briefing"]), scan_policy: GUIDED_ACTIVATION_SCAN_POLICY_CONTRACT }, { required: ["activation_id", "purpose"] }),
13
+ teammate_routine: obj({ chat_id: str({ minLength: 36, maxLength: 36 }), proposal_id: str({ minLength: 1, maxLength: 64 }), report_policy: enumOf(["changes_only", "periodic"]) }, { required: ["chat_id"], description: "An Oxygent routine: your chat with this Agent, which its reports post into. Only you may then start, change or message it." }),
12
14
  }, { required: ["title", "goal", "template", "resources", "actions", "events", "schedule", "budget"] });
13
15
  function agentSessionOperation(id, verb, method, suffix, summary, fields = {}, required = [], spends = false) {
14
16
  const route = `/api/cli/agent/{slug}/sessions${suffix}`;
@@ -29,7 +31,7 @@ export const AGENT_SESSION_OPERATIONS = [
29
31
  agentSessionOperation("agent.sessions.preview", "preview", "POST", "/{session}/preview", "Preview the exact pinned Agent version, resource scope, actions, schedule and caps. Returns the revision and approval token; starts no inference."),
30
32
  agentSessionOperation("agent.sessions.activate", "activate", "POST", "/{session}/activate", "Approve the exact preview revision and activate scoped standing authority. Starts bounded inference. A cap above default_max_credits requires separate approve_above_default=true. Later settings changes require a fresh preview.", { revision: int({ minimum: 1 }), preview_token: str({ minLength: 1 }), approved: bool(), approve_above_default: bool({ description: "Separately approve a per-run cap above the workspace's default Agent ceiling." }) }, ["revision", "preview_token", "approved"], true),
31
33
  agentSessionOperation("agent.sessions.message", "message", "POST", "/{session}/messages", "Queue durable human steering. Draft chat proposes settings for review; active chat resumes the same memory. A bounded run reserves the approved per-run cap.", { message: str({ minLength: 1, maxLength: 16000 }), idempotency_key: str({ minLength: 1, maxLength: 200 }) }, ["message", "idempotency_key"], true),
32
- agentSessionOperation("agent.sessions.run", "run", "POST", "/{session}/run", "Run this active session's next check now, within its approved caps. Refused while a check runs or a cap is reached.", {}, [], true),
34
+ agentSessionOperation("agent.sessions.run", "run", "POST", "/{session}/run", "Run this active session's next check now. Guided routines require a separate bounded preview and approval; other sessions use existing caps. Refused while a check runs.", { approved: bool(), revision: int({ minimum: 1 }), preview_token: str({ minLength: 1, maxLength: 160 }) }, [], true),
33
35
  agentSessionOperation("agent.sessions.webhook", "webhook", "POST", "/{session}/webhook", "Create this session's webhook or rotate its secret (returned once); remove deletes it. A delivery wakes the active session within its caps; its external writes then ask first.", { remove: bool({ description: "Delete the webhook; its URL stops accepting deliveries." }) }),
34
36
  agentSessionOperation("agent.sessions.pause", "pause", "POST", "/{session}/pause", "Pause this ongoing session, retaining conversation and run history."),
35
37
  agentSessionOperation("agent.sessions.resume", "resume", "POST", "/{session}/resume", "Resume this ongoing session. Requires unchanged approved settings.", {}, [], true),
@@ -72,8 +72,8 @@ export const CUSTOMER_OPERATIONS = [
72
72
  operation("limits.provider.get", "/api/cli/limits/provider", "GET", "limits provider list", "oxygen_provider_limits_list", "billing", "Read provider request limits and BYOK warning defaults. MCP defaults to bounded summary pages; follow paging.next_offset with the same filter. HTTP/CLI omit pagination for the full legacy list. 0 credits.", {}, [], { contract: providerLimitListContract, generated: { cli: false, mcp: false } }),
73
73
  operation("limits.provider.set", "/api/cli/limits/provider", "POST", "limits provider set", "oxygen_provider_limits_set", "billing", "Set an enforced provider request limit; admin or owner only. Approve the exact provider, operation and window: raising a cap may permit more provider use. 0 credits.", {}, [], { ...mcpOnly, contract: providerLimitUpdateContract }),
74
74
  operation("limits.provider.delete", "/api/cli/limits/provider", "DELETE", "limits provider delete", "oxygen_provider_limits_delete", "billing", "Clear one provider request-limit override; admin or owner only. Approve the exact selector. Other operation overrides remain; the provider returns to its defaults, whose BYOK threshold may be observe-only. 0 credits.", {}, [], { ...mcpOnly, contract: providerLimitSelectorContract, queryFields: ["provider", "operation"] }),
75
- operation("mailboxes.emailguard.auto_enroll.get", "/api/cli/mailboxes/emailguard/auto-enroll", "GET", "mailboxes emailguard auto-enroll", "oxygen_mailboxes_emailguard_auto_enroll_get", "deliverability", "Read the workspace's automatic monitoring enrollment grant and last sweep.", {}, [], mcpOnly),
76
- operation("mailboxes.emailguard.auto_enroll.set", "/api/cli/mailboxes/emailguard/auto-enroll", "POST", "mailboxes emailguard auto-enroll", "oxygen_mailboxes_emailguard_auto_enroll_set", "deliverability", "Preview, arm or disarm automatic mailbox monitoring. Arming recurring spend requires approved=true and max_credits_per_cycle.", { enabled: bool(), approved: bool(), max_credits_per_cycle: num({ minimum: 0 }), run: bool(), dry_run: bool() }, [], { ...mcpOnly, spends: true }),
75
+ operation("mailboxes.emailguard.auto_enroll.get", "/api/cli/mailboxes/emailguard/auto-enroll", "GET", "mailboxes deliverability auto-enroll", "oxygen_mailboxes_deliverability_auto_enroll_get", "deliverability", "Read the workspace's automatic monitoring enrollment grant and last sweep.", {}, [], mcpOnly),
76
+ operation("mailboxes.emailguard.auto_enroll.set", "/api/cli/mailboxes/emailguard/auto-enroll", "POST", "mailboxes deliverability auto-enroll", "oxygen_mailboxes_deliverability_auto_enroll_set", "deliverability", "Preview, arm or disarm automatic mailbox monitoring. Arming recurring spend requires approved=true and max_credits_per_cycle.", { enabled: bool(), approved: bool(), max_credits_per_cycle: num({ minimum: 0 }), run: bool(), dry_run: bool() }, [], { ...mcpOnly, spends: true }),
77
77
  operation("mailboxes.warmup_profiles.get", "/api/cli/mailboxes/warmup-profile", "GET", "mailboxes warmup profile", "oxygen_mailboxes_warmup_profiles", "deliverability", "Read available OXYGEN Warm-up profiles and current bindings.", {}, [], mcpOnly),
78
78
  operation("mailboxes.warmup_profiles.set", "/api/cli/mailboxes/warmup-profile", "POST", "mailboxes warmup profile", "oxygen_mailboxes_warmup_profile_set", "deliverability", "Preview or apply an OXYGEN Warm-up profile to exact mailboxes or domains; approved=true applies the reviewed scope.", { profile: { oneOf: [text(), json, { type: "null" }] }, budget: nullable(json), domains: list(), mailboxes: list(), all: bool(), approved: bool() }, [], mcpOnly),
79
79
  operation("managed_inboxes.avatars.prepare", "/api/cli/managed-inboxes/avatars", "POST", "managed-inboxes avatars prepare", "oxygen_managed_inbox_avatar_prepare", "deliverability", "Prepare a scoped mailbox profile-picture upload; confirm the stored bytes before using its URL.", { content_type: enumOf(["image/png", "image/jpeg", "image/webp"]), content_length: int({ minimum: 1 }) }, ["content_length"]),
@@ -0,0 +1,177 @@
1
+ export declare const GUIDED_ACTIVATION_REVISIONS_CONTRACT: import("./schema.js").ContractObject<{
2
+ [k: string]: import("./schema.js").StringProperty;
3
+ }, "audience" | "authority" | "configuration" | "copy" | "knowledge" | "recipients" | "schedule" | "sender" | "sequence">;
4
+ export declare const GUIDED_ACTIVATION_SCAN_POLICY_CONTRACT: import("./schema.js").ContractObject<{
5
+ readonly version: {
6
+ type: "integer";
7
+ description?: string;
8
+ minimum?: number;
9
+ maximum?: number;
10
+ "x-clamp"?: true;
11
+ };
12
+ readonly table_id: import("./schema.js").StringProperty;
13
+ readonly chat_id: import("./schema.js").StringProperty;
14
+ readonly policy_digest: import("./schema.js").StringProperty;
15
+ readonly revisions: import("./schema.js").ContractObject<{
16
+ [k: string]: import("./schema.js").StringProperty;
17
+ }, "audience" | "authority" | "configuration" | "copy" | "knowledge" | "recipients" | "schedule" | "sender" | "sequence">;
18
+ readonly sources: import("./schema.js").ArrayProperty<import("./schema.js").ContractObject<{
19
+ readonly source_id: import("./schema.js").StringProperty;
20
+ readonly config_digest: import("./schema.js").StringProperty;
21
+ readonly incremental_mode: {
22
+ type: "string";
23
+ enum: readonly ("cursor" | "watermark")[];
24
+ description?: string;
25
+ };
26
+ readonly person_key_field: import("./schema.js").StringProperty;
27
+ }, "config_digest" | "incremental_mode" | "person_key_field" | "source_id">>;
28
+ readonly exclude_dnc: import("./schema.js").BooleanProperty;
29
+ readonly exclude_contacted: import("./schema.js").BooleanProperty;
30
+ readonly review_only: import("./schema.js").BooleanProperty;
31
+ readonly max_examined: {
32
+ type: "integer";
33
+ description?: string;
34
+ minimum?: number;
35
+ maximum?: number;
36
+ "x-clamp"?: true;
37
+ };
38
+ readonly max_new_leads: {
39
+ type: "integer";
40
+ description?: string;
41
+ minimum?: number;
42
+ maximum?: number;
43
+ "x-clamp"?: true;
44
+ };
45
+ readonly max_provider_pages: {
46
+ type: "integer";
47
+ description?: string;
48
+ minimum?: number;
49
+ maximum?: number;
50
+ "x-clamp"?: true;
51
+ };
52
+ readonly max_provider_credits: {
53
+ type: "number";
54
+ description?: string;
55
+ minimum?: number;
56
+ maximum?: number;
57
+ "x-clamp"?: true;
58
+ };
59
+ readonly max_inference_credits: {
60
+ type: "number";
61
+ description?: string;
62
+ minimum?: number;
63
+ maximum?: number;
64
+ "x-clamp"?: true;
65
+ };
66
+ }, "chat_id" | "exclude_contacted" | "exclude_dnc" | "max_examined" | "max_inference_credits" | "max_new_leads" | "max_provider_credits" | "max_provider_pages" | "policy_digest" | "review_only" | "revisions" | "sources" | "table_id" | "version">;
67
+ export declare const GUIDED_ACTIVATION_LINK_CONTRACT: import("./schema.js").ContractObject<{
68
+ readonly revision: {
69
+ type: "integer";
70
+ description?: string;
71
+ minimum?: number;
72
+ maximum?: number;
73
+ "x-clamp"?: true;
74
+ };
75
+ readonly resource: {
76
+ type: "string";
77
+ enum: readonly ("first_leads_table" | "knowledge" | "sender" | "sequence")[];
78
+ description?: string;
79
+ };
80
+ readonly resource_id: import("./schema.js").StringProperty;
81
+ }, "resource" | "resource_id" | "revision">;
82
+ export declare const GUIDED_ACTIVATION_START_CONTRACT: import("./schema.js").ContractObject<{
83
+ readonly operating_path: {
84
+ type: "string";
85
+ enum: readonly ("own_agent" | "oxygent")[];
86
+ description?: string;
87
+ };
88
+ }, "operating_path">;
89
+ import type { OperationSpec } from "./operations.js";
90
+ export declare const GUIDED_ACTIVATION_ROUTINES_PREVIEW_CONTRACT: import("./schema.js").ContractObject<{
91
+ readonly revision: {
92
+ type: "integer";
93
+ description?: string;
94
+ minimum?: number;
95
+ maximum?: number;
96
+ "x-clamp"?: true;
97
+ };
98
+ readonly timezone: import("./schema.js").StringProperty;
99
+ readonly timezone_confirmed: import("./schema.js").BooleanProperty;
100
+ readonly scan_time: import("./schema.js").StringProperty;
101
+ readonly briefing_time: import("./schema.js").StringProperty;
102
+ readonly source_ids: import("./schema.js").ArrayProperty<import("./schema.js").StringProperty>;
103
+ readonly max_examined: {
104
+ type: "integer";
105
+ description?: string;
106
+ minimum?: number;
107
+ maximum?: number;
108
+ "x-clamp"?: true;
109
+ };
110
+ readonly max_new_leads: {
111
+ type: "integer";
112
+ description?: string;
113
+ minimum?: number;
114
+ maximum?: number;
115
+ "x-clamp"?: true;
116
+ };
117
+ readonly max_provider_pages: {
118
+ type: "integer";
119
+ description?: string;
120
+ minimum?: number;
121
+ maximum?: number;
122
+ "x-clamp"?: true;
123
+ };
124
+ readonly max_provider_credits: {
125
+ type: "number";
126
+ description?: string;
127
+ minimum?: number;
128
+ maximum?: number;
129
+ "x-clamp"?: true;
130
+ };
131
+ readonly scan_per_run_credits: {
132
+ type: "number";
133
+ description?: string;
134
+ minimum?: number;
135
+ maximum?: number;
136
+ "x-clamp"?: true;
137
+ };
138
+ readonly scan_daily_credits: {
139
+ type: "number";
140
+ description?: string;
141
+ minimum?: number;
142
+ maximum?: number;
143
+ "x-clamp"?: true;
144
+ };
145
+ readonly briefing_per_run_credits: {
146
+ type: "number";
147
+ description?: string;
148
+ minimum?: number;
149
+ maximum?: number;
150
+ "x-clamp"?: true;
151
+ };
152
+ readonly briefing_daily_credits: {
153
+ type: "number";
154
+ description?: string;
155
+ minimum?: number;
156
+ maximum?: number;
157
+ "x-clamp"?: true;
158
+ };
159
+ }, "briefing_daily_credits" | "briefing_per_run_credits" | "briefing_time" | "max_examined" | "max_new_leads" | "max_provider_credits" | "max_provider_pages" | "revision" | "scan_daily_credits" | "scan_per_run_credits" | "scan_time" | "source_ids" | "timezone" | "timezone_confirmed">;
160
+ export declare const GUIDED_ACTIVATION_ROUTINE_ACTIVATE_CONTRACT: import("./schema.js").ContractObject<{
161
+ readonly purpose: {
162
+ type: "string";
163
+ enum: readonly ("lead_scan" | "morning_briefing")[];
164
+ description?: string;
165
+ };
166
+ readonly revision: {
167
+ type: "integer";
168
+ description?: string;
169
+ minimum?: number;
170
+ maximum?: number;
171
+ "x-clamp"?: true;
172
+ };
173
+ readonly preview_token: import("./schema.js").StringProperty;
174
+ readonly approved: import("./schema.js").BooleanProperty;
175
+ readonly approve_above_default: import("./schema.js").BooleanProperty;
176
+ }, "preview_token" | "purpose" | "revision">;
177
+ export declare const GUIDED_ACTIVATION_OPERATIONS: readonly OperationSpec[];
@@ -0,0 +1,59 @@
1
+ import { arr, bool, enumOf, int, num, obj, str } from "./schema.js";
2
+ import { GUIDED_ACTIVATION_REVISIONS } from "../guided-activation.js";
3
+ const id = () => str({ minLength: 1, maxLength: 160 });
4
+ export const GUIDED_ACTIVATION_REVISIONS_CONTRACT = obj(Object.fromEntries(GUIDED_ACTIVATION_REVISIONS.map(key => [key, id()])), { required: [...GUIDED_ACTIVATION_REVISIONS] });
5
+ export const GUIDED_ACTIVATION_SCAN_POLICY_CONTRACT = obj({
6
+ version: int({ minimum: 1, maximum: 1 }), table_id: id(), chat_id: id(), policy_digest: id(),
7
+ revisions: GUIDED_ACTIVATION_REVISIONS_CONTRACT,
8
+ sources: arr(obj({ source_id: id(), config_digest: id(), incremental_mode: enumOf(["cursor", "watermark"]), person_key_field: id() }, { required: ["source_id", "config_digest", "incremental_mode", "person_key_field"] }), { minItems: 1, maxItems: 20 }),
9
+ exclude_dnc: bool(), exclude_contacted: bool(), review_only: bool(),
10
+ max_examined: int({ minimum: 1, maximum: 1000 }), max_new_leads: int({ minimum: 1, maximum: 100 }), max_provider_pages: int({ minimum: 1, maximum: 100 }),
11
+ max_provider_credits: num({ minimum: 0, maximum: 25000 }), max_inference_credits: num({ minimum: 0, maximum: 25000 }),
12
+ }, { required: ["version", "table_id", "chat_id", "policy_digest", "revisions", "sources", "exclude_dnc", "exclude_contacted", "review_only", "max_examined", "max_new_leads", "max_provider_pages", "max_provider_credits", "max_inference_credits"] });
13
+ export const GUIDED_ACTIVATION_LINK_CONTRACT = obj({
14
+ revision: int({ minimum: 1 }), resource: enumOf(["knowledge", "first_leads_table", "sequence", "sender"]), resource_id: str({ minLength: 1, maxLength: 160 }),
15
+ }, { required: ["revision", "resource", "resource_id"] });
16
+ export const GUIDED_ACTIVATION_START_CONTRACT = obj({ operating_path: enumOf(["own_agent", "oxygent"]) }, { required: ["operating_path"] });
17
+ export const GUIDED_ACTIVATION_ROUTINES_PREVIEW_CONTRACT = obj({
18
+ revision: int({ minimum: 1, description: "Current activation revision from status/get (positive integer)." }),
19
+ timezone: str({ minLength: 1, maxLength: 80, description: "Confirmed IANA timezone, e.g. Europe/Berlin." }),
20
+ timezone_confirmed: bool({ description: "True only after the user confirms this timezone." }),
21
+ scan_time: str({ pattern: "^(?:[01]\\d|2[0-3]):[0-5]\\d$", description: "Scan start in HH:mm (00:00-23:59), local to timezone." }),
22
+ briefing_time: str({ pattern: "^(?:[01]\\d|2[0-3]):[0-5]\\d$", description: "Briefing start in HH:mm (00:00-23:59), local to timezone." }),
23
+ source_ids: arr(str({ minLength: 1, maxLength: 160 }), { minItems: 1, maxItems: 20,
24
+ description: "1-20 unique source IDs, comma-separated on CLI: table:<UUID> for a separate source Table or completed native people-search ingestion-run UUID." }),
25
+ max_examined: int({ minimum: 1, maximum: 1000, description: "Maximum source people examined per scan: integer 1-1000." }),
26
+ max_new_leads: int({ minimum: 1, maximum: 100, description: "Maximum review-only leads per scan: integer 1-100; no greater than max_examined." }),
27
+ max_provider_pages: int({ minimum: 1, maximum: 100, description: "Maximum provider pages per scan: integer 1-100." }),
28
+ max_provider_credits: num({ minimum: 0, maximum: 25000, description: "Provider credit ceiling per scan: 0-25000; native Table sources quote 0." }),
29
+ scan_per_run_credits: num({ minimum: .01, maximum: 25000, description: "Scan credit ceiling per run: 0.01-25000." }),
30
+ scan_daily_credits: num({ minimum: .01, description: "Scan credit ceiling per rolling 24h: at least 0.01 and scan_per_run_credits." }),
31
+ briefing_per_run_credits: num({ minimum: .01, maximum: 25000, description: "Briefing credit ceiling per run: 0.01-25000." }),
32
+ briefing_daily_credits: num({ minimum: .01, description: "Briefing credit ceiling per rolling 24h: at least 0.01 and briefing_per_run_credits." }),
33
+ }, { required: ["revision", "timezone", "timezone_confirmed", "scan_time", "briefing_time", "source_ids", "max_examined", "max_new_leads", "max_provider_pages", "max_provider_credits", "scan_per_run_credits", "scan_daily_credits", "briefing_per_run_credits", "briefing_daily_credits"] });
34
+ export const GUIDED_ACTIVATION_ROUTINE_ACTIVATE_CONTRACT = obj({
35
+ purpose: enumOf(["lead_scan", "morning_briefing"]), revision: int({ minimum: 1 }), preview_token: str({ minLength: 1, maxLength: 160 }), approved: bool(), approve_above_default: bool(),
36
+ }, { required: ["purpose", "revision", "preview_token"] });
37
+ export const GUIDED_ACTIVATION_OPERATIONS = [
38
+ { id: "onboarding.activation.status", route: "/api/cli/onboarding/activation", method: "GET", contract: obj({}),
39
+ cli: { path: ["onboarding", "activation", "status"], summary: "Resume your guided onboarding and native resource links. Enabled genuine dev only; current non-client workspace members." },
40
+ mcp: { name: "oxygen_onboarding_activation_status", title: "Oxygen: Onboarding status", description: "Read your resumable guided onboarding and native links. Enabled genuine dev only; current non-client workspace members.", pack: "agents" }, mutates: false, spends: false },
41
+ { id: "onboarding.activation.start", route: "/api/cli/onboarding/activation", method: "POST", contract: GUIDED_ACTIVATION_START_CONTRACT,
42
+ cli: { path: ["onboarding", "activation", "start"], summary: "Prepare your activation and private Oxygent chat. Free; no message send or outreach." },
43
+ mcp: { name: "oxygen_onboarding_activation_start", title: "Oxygen: Start onboarding record", description: "Prepare your activation with operating_path own_agent or oxygent. The oxygent path binds its native workspace Agent and creator-private chat. Free; no turn, inference, provider or outreach dispatch. Enabled genuine dev only; current non-client workspace members.", pack: "agents" }, mutates: true, spends: false },
44
+ { id: "onboarding.activation.get", route: "/api/cli/onboarding/activation/{activationId}", method: "GET", parameters: ["activationId"], contract: obj({}),
45
+ cli: { path: ["onboarding", "activation", "get"], summary: "Read your activation by ID and its native resource links." },
46
+ mcp: { name: "oxygen_onboarding_activation_get", title: "Oxygen: Read onboarding record", description: "Read your activation by activationId. Only its creator can read it. Enabled genuine dev only; current non-client workspace members.", pack: "agents" }, mutates: false, spends: false },
47
+ { id: "onboarding.activation.link", route: "/api/cli/onboarding/activation/{activationId}/resources", method: "POST", parameters: ["activationId"], contract: GUIDED_ACTIVATION_LINK_CONTRACT,
48
+ cli: { path: ["onboarding", "activation", "link"], summary: "Link an existing native onboarding resource at the expected revision. Does not create, enroll or send." },
49
+ mcp: { name: "oxygen_onboarding_activation_link", title: "Oxygen: Link onboarding resource", description: "Link an existing Knowledge page, First leads Table, pilot Sequence or LinkedIn sender to your activationId at revision. Native ownership and Sequence binding are verified; no create, enrollment or send.", pack: "agents" }, mutates: true, spends: false },
50
+ { id: "onboarding.activation.first_leads", route: "/api/cli/onboarding/activation/{activationId}/first-leads", method: "POST", bodyRequired: true, parameters: ["activationId"], contract: obj({}),
51
+ cli: { path: ["onboarding", "activation", "first-leads"], summary: "Create or resume the activation-owned First leads Table. Free; no discovery, enrichment or outreach." },
52
+ mcp: { name: "oxygen_onboarding_activation_first_leads", title: "Oxygen: First leads Table", description: "Create or resume one native First leads Table for your activationId. Free; no discovery, enrichment, enrollment or outreach. Use native search/ingestion separately.", pack: "agents" }, mutates: true, spends: false },
53
+ { id: "onboarding.activation.routines_preview", route: "/api/cli/onboarding/activation/{activationId}/routines/preview", method: "POST", parameters: ["activationId"], contract: GUIDED_ACTIVATION_ROUTINES_PREVIEW_CONTRACT,
54
+ cli: { path: ["onboarding", "activation", "routines-preview"], summary: "Review two explicitly configured draft routines, exact native source scope and caps. Arms no schedule." },
55
+ mcp: { name: "oxygen_onboarding_activation_routines_preview", title: "Oxygen: Review daily routines", description: "Build native scan/briefing drafts and exact previews for activationId. Requires confirmed IANA timezone, both local times, source_ids and caps. Native Table sources quote zero; original ingestion sources need a current quote. Arms no schedule.", pack: "agents" }, mutates: true, spends: false },
56
+ { id: "onboarding.activation.routine_activate", route: "/api/cli/onboarding/activation/{activationId}/routines/activate", method: "POST", parameters: ["activationId"], contract: GUIDED_ACTIVATION_ROUTINE_ACTIVATE_CONTRACT,
57
+ cli: { path: ["onboarding", "activation", "routine-activate"], summary: "Activate one exact routine preview at its native revision. Requires approval or valid current creator-chat auto-approve authority." },
58
+ mcp: { name: "oxygen_onboarding_activation_routine_activate", title: "Oxygen: Activate daily routine", description: "Activate one lead_scan or morning_briefing at exact native revision and preview_token. Requires approved=true or current auto-approve authority in the creator's bound private chat. Schedules are independent and remain capped; no automatic enrollment.", pack: "agents" }, mutates: true, spends: true },
59
+ ];
@@ -12,6 +12,7 @@ import { BROWSER_OPERATIONS } from "./browser-operations.js";
12
12
  import { TABLE_FEED_UPDATE_OPERATION } from "./table-feed-update.js";
13
13
  import { GEO_OPERATIONS } from "./geo.js";
14
14
  import { CUSTOMER_OPERATIONS } from "./customer-operations.js";
15
+ import { GUIDED_ACTIVATION_OPERATIONS } from "./guided-activation.js";
15
16
  import { AGENT_SESSION_OPERATIONS } from "./agent-sessions.js";
16
17
  import { TEAMMATE_OPERATIONS } from "./teammates.js";
17
18
  import { WORKSPACE_SKILL_OPERATIONS } from "./workspace-skills.js";
@@ -91,6 +92,6 @@ export function operationFormData(spec, body) {
91
92
  }
92
93
  return form;
93
94
  }
94
- export const OPERATION_SPECS = [SENDER_LIMITS_SET_OPERATION, CONNECTOR_UPDATE_OPERATION, CONNECTOR_EVENTS_REVOKE_OPERATION, WORKSPACE_PAGES_GET_OPERATION, WORKSPACE_PAGES_UPDATE_OPERATION, TABLE_FEED_UPDATE_OPERATION, ...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS];
95
- export const CLI_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.cli !== false);
96
- export const MCP_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.mcp !== false);
95
+ export const OPERATION_SPECS = [SENDER_LIMITS_SET_OPERATION, CONNECTOR_UPDATE_OPERATION, CONNECTOR_EVENTS_REVOKE_OPERATION, WORKSPACE_PAGES_GET_OPERATION, WORKSPACE_PAGES_UPDATE_OPERATION, TABLE_FEED_UPDATE_OPERATION, ...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...GUIDED_ACTIVATION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS];
96
+ export const CLI_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...GUIDED_ACTIVATION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.cli !== false);
97
+ export const MCP_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...GUIDED_ACTIVATION_OPERATIONS, ...TEAMMATE_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.mcp !== false);
@@ -1,3 +1,4 @@
1
+ import { guidedActivationEnabled } from "./guided-activation.js";
1
2
  import { isStaffEmail } from "./staff.js";
2
3
  export function copilotAudience(env = process.env) {
3
4
  const deployment = env.OXYGEN_DEPLOY_ENV?.trim().toLowerCase();
@@ -26,7 +27,7 @@ export function isCopilotAvailableTo(env, viewer) {
26
27
  const audience = copilotAudience(env);
27
28
  if (audience === "off" || !viewer.member || viewer.oxygenRole === "client")
28
29
  return false;
29
- return audience === "all" || isStaffEmail(viewer.email, env);
30
+ return audience === "all" || guidedActivationEnabled(env) || isStaffEmail(viewer.email, env);
30
31
  }
31
32
  function normalizedFlag(env) {
32
33
  return env.OXYGEN_COPILOT_ENABLED?.trim().toLowerCase() ?? "";
@@ -1,3 +1,4 @@
1
+ import { normalizeQuotedCredits, roundQuotedCredits } from "./billing-money.js";
1
2
  const DEFAULT_HEADROOM_MULTIPLIER = 1.25;
2
3
  /**
3
4
  * Size a ceiling from the EXPECTED cost plus a variance band, rather than from
@@ -39,9 +40,9 @@ function expectedCreditCeiling(input) {
39
40
  return Math.max(1, Math.ceil(ceiling));
40
41
  }
41
42
  export function buildCreditGuidance(input) {
42
- const estimatedCredits = normalizeCreditValue(input.estimatedCredits);
43
- const availableCredits = normalizeCreditValue(input.availableCredits);
44
- const expectedCredits = normalizeCreditValue(input.expectedCredits);
43
+ const estimatedCredits = normalizeQuotedCredits(input.estimatedCredits);
44
+ const availableCredits = normalizeQuotedCredits(input.availableCredits);
45
+ const expectedCredits = normalizeQuotedCredits(input.expectedCredits);
45
46
  const headroomMultiplier = normalizeMultiplier(input.headroomMultiplier);
46
47
  // No expected figure supplied => byte-identical to the pre-existing behaviour,
47
48
  // so publishing / sequences / AI callers are untouched by this change.
@@ -49,7 +50,7 @@ export function buildCreditGuidance(input) {
49
50
  ? recommendedCreditCeiling(estimatedCredits, headroomMultiplier)
50
51
  : expectedCreditCeiling({
51
52
  expectedCredits,
52
- worstCaseCreditsPerRow: normalizeCreditValue(input.worstCaseCreditsPerRow),
53
+ worstCaseCreditsPerRow: normalizeQuotedCredits(input.worstCaseCreditsPerRow),
53
54
  worstCaseTotal: estimatedCredits,
54
55
  rowCount: typeof input.rowCount === "number" && Number.isFinite(input.rowCount)
55
56
  ? Math.max(0, Math.floor(input.rowCount))
@@ -92,7 +93,7 @@ export function buildCreditGuidance(input) {
92
93
  recommended_max_credits: recommendedMaxCredits,
93
94
  available_credits: availableCredits,
94
95
  credit_posture: "sufficient",
95
- credit_guidance: `At the fixed per-row price, the selected rows are estimated at ${formatCreditsForCopy(estimatedCredits)} credits. `
96
+ credit_guidance: `The selected rows are estimated at ${formatCreditsForCopy(estimatedCredits)} credits. `
96
97
  + "recommended_max_credits is a safety ceiling; unused credits are not spent.",
97
98
  };
98
99
  }
@@ -126,14 +127,14 @@ export function buildCreditGuidance(input) {
126
127
  return {
127
128
  ...expectedFields,
128
129
  estimated_credits: estimatedCredits,
129
- recommended_max_credits: availableCredits > 0 ? roundCreditValue(availableCredits) : null,
130
+ recommended_max_credits: availableCredits > 0 ? roundQuotedCredits(availableCredits) : null,
130
131
  available_credits: availableCredits,
131
132
  credit_posture: "tight",
132
133
  credit_guidance: "Credits are tight for the recommended ceiling; ask before running, reduce scope, or top up credits.",
133
134
  };
134
135
  }
135
136
  function recommendedCreditCeiling(estimatedCredits, headroomMultiplier = DEFAULT_HEADROOM_MULTIPLIER) {
136
- const normalized = normalizeCreditValue(estimatedCredits);
137
+ const normalized = normalizeQuotedCredits(estimatedCredits);
137
138
  if (normalized === null)
138
139
  return null;
139
140
  if (normalized === 0)
@@ -151,11 +152,3 @@ function normalizeMultiplier(value) {
151
152
  function formatCreditsForCopy(value) {
152
153
  return value.toLocaleString("en-US", { maximumFractionDigits: 3 });
153
154
  }
154
- function normalizeCreditValue(value) {
155
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
156
- return null;
157
- return roundCreditValue(value);
158
- }
159
- function roundCreditValue(value) {
160
- return Math.round(value * 1000) / 1000;
161
- }
@@ -0,0 +1,65 @@
1
+ /** Public DNS configuration evidence, never a received-message authentication verdict. */
2
+ export declare const DOMAIN_AUTHENTICATION_INTERVAL_MS: number;
3
+ export declare const DOMAIN_AUTHENTICATION_CHECKS: readonly ["spf", "dkim", "dmarc", "mx", "nameservers"];
4
+ export type DomainAuthenticationCheckName = (typeof DOMAIN_AUTHENTICATION_CHECKS)[number];
5
+ export type DomainAuthenticationState = "configured" | "issues" | "unknown";
6
+ export type DomainAuthenticationCheck = {
7
+ state: DomainAuthenticationState;
8
+ summary: string;
9
+ nextAction: string | null;
10
+ records: Array<{
11
+ name: string;
12
+ type: string;
13
+ content: string;
14
+ }>;
15
+ };
16
+ export type DomainAuthenticationObservation = {
17
+ domain: string;
18
+ source: "public_dns";
19
+ checkedAt: string;
20
+ state: DomainAuthenticationState;
21
+ complete: boolean;
22
+ checks: Record<DomainAuthenticationCheckName, DomainAuthenticationCheck>;
23
+ dkimSelectors: string[];
24
+ dmarcPolicy: string | null;
25
+ dmarcPolicyDomain: string | null;
26
+ spfLookupTerms: number | null;
27
+ spfLookupComplete: boolean;
28
+ messageAlignment: "unknown";
29
+ };
30
+ export type DomainAuthenticationSnapshot = {
31
+ domain: string;
32
+ observation: DomainAuthenticationObservation | null;
33
+ lastAttempt: DomainAuthenticationObservation | null;
34
+ lastAttemptAt: string | null;
35
+ lastAttemptFailed: boolean;
36
+ nextCheckAt: string | null;
37
+ monitoring?: {
38
+ enabled: boolean;
39
+ eligibleMailboxCount: number;
40
+ reason: string;
41
+ manualCheckEnablesMonitoring: false;
42
+ };
43
+ };
44
+ export type DomainAuthenticationRow = DomainAuthenticationSnapshot & {
45
+ mailboxCount: number;
46
+ };
47
+ export declare function domainAuthenticationStale(snapshot: DomainAuthenticationSnapshot, now?: number): boolean;
48
+ export declare function domainAuthenticationNeedsAttention(snapshot: DomainAuthenticationSnapshot, now?: number): boolean;
49
+ /** Eligibility comes from connected mailboxes, never from saving a DNS observation. */
50
+ export declare function domainAuthenticationWithMonitoring(snapshot: DomainAuthenticationSnapshot, eligibleMailboxCount: number): DomainAuthenticationSnapshot;
51
+ /** Compact list projection keeps raw DNS evidence in the per-domain detail read. */
52
+ export declare function domainAuthenticationSummary(row: DomainAuthenticationRow, now?: number): {
53
+ domain: string;
54
+ mailboxCount: number;
55
+ monitored: boolean;
56
+ state: DomainAuthenticationState;
57
+ checks: {
58
+ [k: string]: DomainAuthenticationState;
59
+ } | null;
60
+ checkedAt: string | null;
61
+ lastAttemptAt: string | null;
62
+ lastAttemptFailed: boolean;
63
+ stale: boolean;
64
+ nextCheckAt: string | null;
65
+ };
@@ -0,0 +1,54 @@
1
+ /** Public DNS configuration evidence, never a received-message authentication verdict. */
2
+ export const DOMAIN_AUTHENTICATION_INTERVAL_MS = 24 * 60 * 60_000;
3
+ export const DOMAIN_AUTHENTICATION_CHECKS = [
4
+ "spf",
5
+ "dkim",
6
+ "dmarc",
7
+ "mx",
8
+ "nameservers",
9
+ ];
10
+ export function domainAuthenticationStale(snapshot, now = Date.now()) {
11
+ return (!snapshot.observation ||
12
+ now - Date.parse(snapshot.observation.checkedAt) >=
13
+ DOMAIN_AUTHENTICATION_INTERVAL_MS);
14
+ }
15
+ export function domainAuthenticationNeedsAttention(snapshot, now = Date.now()) {
16
+ return snapshot.lastAttemptFailed || domainAuthenticationStale(snapshot, now) || snapshot.observation?.state !== "configured";
17
+ }
18
+ /** Eligibility comes from connected mailboxes, never from saving a DNS observation. */
19
+ export function domainAuthenticationWithMonitoring(snapshot, eligibleMailboxCount) {
20
+ const enabled = eligibleMailboxCount > 0;
21
+ return {
22
+ ...snapshot,
23
+ nextCheckAt: enabled ? snapshot.nextCheckAt : null,
24
+ monitoring: {
25
+ enabled,
26
+ eligibleMailboxCount,
27
+ reason: enabled
28
+ ? "Daily checks cover non-disabled connected sending mailboxes, including paused mailboxes."
29
+ : "No eligible sending mailboxes. Disabled, deleted and mailbox-free domains are not checked daily; a free manual check saves one observation without enabling monitoring.",
30
+ manualCheckEnablesMonitoring: false,
31
+ },
32
+ };
33
+ }
34
+ /** Compact list projection keeps raw DNS evidence in the per-domain detail read. */
35
+ export function domainAuthenticationSummary(row, now = Date.now()) {
36
+ const observation = row.observation ?? row.lastAttempt;
37
+ return {
38
+ domain: row.domain,
39
+ mailboxCount: row.mailboxCount,
40
+ monitored: row.mailboxCount > 0,
41
+ state: observation?.state ?? "unknown",
42
+ checks: observation
43
+ ? Object.fromEntries(DOMAIN_AUTHENTICATION_CHECKS.map((key) => [
44
+ key,
45
+ observation.checks[key].state,
46
+ ]))
47
+ : null,
48
+ checkedAt: observation?.checkedAt ?? null,
49
+ lastAttemptAt: row.lastAttemptAt,
50
+ lastAttemptFailed: row.lastAttemptFailed,
51
+ stale: domainAuthenticationStale(row, now),
52
+ nextCheckAt: row.nextCheckAt,
53
+ };
54
+ }
@@ -0,0 +1,5 @@
1
+ /** Explicit mailbox infrastructure, independent of transport or domain density. */
2
+ export declare function isEntraMailbox(metadata: unknown): boolean;
3
+ /** Founder 2026-10-15: campaign ceiling across all Sequences, separate from warm-up. */
4
+ export declare const ENTRA_CAMPAIGN_DAILY_CAP = 2;
5
+ export declare function mailboxInfrastructureDailyCap(metadata: unknown): number | null;
@@ -0,0 +1,13 @@
1
+ /** Explicit mailbox infrastructure, independent of transport or domain density. */
2
+ export function isEntraMailbox(metadata) {
3
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
4
+ return false;
5
+ const record = metadata;
6
+ const platform = record.infrastructure_platform ?? record.managed_platform;
7
+ return platform === "azure" || platform === "microsoft_azure" || platform === "entra";
8
+ }
9
+ /** Founder 2026-10-15: campaign ceiling across all Sequences, separate from warm-up. */
10
+ export const ENTRA_CAMPAIGN_DAILY_CAP = 2;
11
+ export function mailboxInfrastructureDailyCap(metadata) {
12
+ return isEntraMailbox(metadata) ? ENTRA_CAMPAIGN_DAILY_CAP : null;
13
+ }