@oxygen-agent/cli 1.1011.326 → 1.1011.440

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 (98) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +15 -4
  3. package/dist/connectors-commands.d.ts +4 -1
  4. package/dist/connectors-commands.js +84 -10
  5. package/dist/contract-flags.d.ts +2 -0
  6. package/dist/contract-flags.js +26 -11
  7. package/dist/crm-support-flags.d.ts +11 -0
  8. package/dist/crm-support-flags.js +60 -0
  9. package/dist/customer-operation-commands.d.ts +5 -0
  10. package/dist/customer-operation-commands.js +101 -0
  11. package/dist/database-query-flags.d.ts +2 -0
  12. package/dist/database-query-flags.js +6 -0
  13. package/dist/help.d.ts +3 -1
  14. package/dist/help.js +43 -0
  15. package/dist/http-client.d.ts +2 -0
  16. package/dist/http-client.js +41 -1
  17. package/dist/index.js +640 -234
  18. package/dist/knowledge-repository-commands.js +12 -14
  19. package/dist/mailbox-ramp-notice.d.ts +11 -0
  20. package/dist/mailbox-ramp-notice.js +53 -0
  21. package/dist/settings-commands.d.ts +11 -0
  22. package/dist/settings-commands.js +73 -0
  23. package/dist/skills.d.ts +5 -4
  24. package/dist/skills.js +106 -79
  25. package/dist/tool-catalog-detail-command.d.ts +7 -0
  26. package/dist/tool-catalog-detail-command.js +19 -0
  27. package/dist/tool-catalog-search-window.d.ts +6 -0
  28. package/dist/tool-catalog-search-window.js +12 -0
  29. package/dist/transcript.js +1 -0
  30. package/node_modules/@oxygen/shared/dist/bounded-read-result.d.ts +4 -0
  31. package/node_modules/@oxygen/shared/dist/bounded-read-result.js +25 -0
  32. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +47 -1
  33. package/node_modules/@oxygen/shared/dist/capability-discovery.js +625 -39
  34. package/node_modules/@oxygen/shared/dist/connection-approval.d.ts +2 -0
  35. package/node_modules/@oxygen/shared/dist/connection-approval.js +2 -0
  36. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.d.ts +2 -0
  37. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +110 -0
  38. package/node_modules/@oxygen/shared/dist/contracts/connectors.d.ts +32 -6
  39. package/node_modules/@oxygen/shared/dist/contracts/connectors.js +56 -4
  40. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.d.ts +4 -0
  41. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.js +39 -0
  42. package/node_modules/@oxygen/shared/dist/contracts/crm-enrichment-read.d.ts +21 -0
  43. package/node_modules/@oxygen/shared/dist/contracts/crm-enrichment-read.js +15 -0
  44. package/node_modules/@oxygen/shared/dist/contracts/crm-support.d.ts +117 -0
  45. package/node_modules/@oxygen/shared/dist/contracts/crm-support.js +43 -0
  46. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.d.ts +2 -0
  47. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +131 -0
  48. package/node_modules/@oxygen/shared/dist/contracts/dashboard-tabs.d.ts +3 -0
  49. package/node_modules/@oxygen/shared/dist/contracts/dashboard-tabs.js +2 -0
  50. package/node_modules/@oxygen/shared/dist/contracts/database.d.ts +30 -0
  51. package/node_modules/@oxygen/shared/dist/contracts/database.js +29 -0
  52. package/node_modules/@oxygen/shared/dist/contracts/integration-connect.d.ts +4 -0
  53. package/node_modules/@oxygen/shared/dist/contracts/integration-connect.js +7 -0
  54. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.d.ts +43 -0
  55. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.js +20 -0
  56. package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.d.ts +12 -0
  57. package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.js +8 -0
  58. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +48 -2
  59. package/node_modules/@oxygen/shared/dist/contracts/operations.js +68 -2
  60. package/node_modules/@oxygen/shared/dist/contracts/table-grid-query.d.ts +4 -0
  61. package/node_modules/@oxygen/shared/dist/contracts/table-grid-query.js +6 -0
  62. package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.d.ts +21 -0
  63. package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.js +27 -0
  64. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-detail.d.ts +19 -0
  65. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-detail.js +10 -0
  66. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-search-window.d.ts +12 -0
  67. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-search-window.js +7 -0
  68. package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.d.ts +17 -0
  69. package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.js +49 -0
  70. package/node_modules/@oxygen/shared/dist/decision-ledger-parser.d.ts +32 -0
  71. package/node_modules/@oxygen/shared/dist/decision-ledger-parser.js +303 -0
  72. package/node_modules/@oxygen/shared/dist/decision-ledger.d.ts +10 -0
  73. package/node_modules/@oxygen/shared/dist/decision-ledger.generated.d.ts +2 -0
  74. package/node_modules/@oxygen/shared/dist/decision-ledger.generated.js +717 -0
  75. package/node_modules/@oxygen/shared/dist/decision-ledger.js +20 -0
  76. package/node_modules/@oxygen/shared/dist/email-campaign-ramp.d.ts +104 -0
  77. package/node_modules/@oxygen/shared/dist/email-campaign-ramp.js +250 -0
  78. package/node_modules/@oxygen/shared/dist/mcp-event-contract.d.ts +10 -7
  79. package/node_modules/@oxygen/shared/dist/mcp-event-contract.js +11 -7
  80. package/node_modules/@oxygen/shared/dist/mcp-table-widget.d.ts +50 -0
  81. package/node_modules/@oxygen/shared/dist/mcp-table-widget.js +24 -0
  82. package/node_modules/@oxygen/shared/dist/plan-band.d.ts +8 -0
  83. package/node_modules/@oxygen/shared/dist/plan-band.js +12 -0
  84. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +20 -0
  85. package/node_modules/@oxygen/shared/dist/plan-limits.js +41 -0
  86. package/node_modules/@oxygen/shared/dist/provider-input-value.d.ts +5 -0
  87. package/node_modules/@oxygen/shared/dist/provider-input-value.js +10 -0
  88. package/node_modules/@oxygen/shared/dist/sequences.d.ts +33 -4
  89. package/node_modules/@oxygen/shared/dist/sequences.js +48 -13
  90. package/node_modules/@oxygen/shared/dist/user-capability-routing.js +17 -0
  91. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  92. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  93. package/node_modules/@oxygen/shared/package.json +8 -6
  94. package/package.json +1 -1
  95. package/node_modules/@oxygen/shared/dist/copilot-skills.d.ts +0 -15
  96. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +0 -31
  97. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +0 -41
  98. package/node_modules/@oxygen/shared/dist/copilot-skills.js +0 -6
@@ -0,0 +1,20 @@
1
+ import { DECISION_LEDGER_ENTRIES } from "./decision-ledger.generated.js";
2
+ export { parseDecisionLedger } from "./decision-ledger-parser.js";
3
+ const DECISIONS_BY_ID = new Map(DECISION_LEDGER_ENTRIES.map((entry) => [entry.id, entry]));
4
+ /**
5
+ * The ledger entry for a decision id. An exact id wins ("D34a" is its own
6
+ * decision); otherwise an answer-option suffix resolves to its base decision
7
+ * ("D19a" and "D19(a)" are D19's option (a)).
8
+ */
9
+ export function getDecision(id) {
10
+ const key = id.trim();
11
+ const exact = DECISIONS_BY_ID.get(key);
12
+ if (exact)
13
+ return exact;
14
+ const base = /^(D\d+)(?:[a-z]|\([a-z]\))$/.exec(key)?.[1];
15
+ return base ? DECISIONS_BY_ID.get(base) ?? null : null;
16
+ }
17
+ /** Every ledger entry, sorted by decision id. */
18
+ export function listDecisions() {
19
+ return DECISION_LEDGER_ENTRIES;
20
+ }
@@ -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;
@@ -0,0 +1,250 @@
1
+ // Per-mailbox campaign ramp-up: how much campaign email an inbox may send on each
2
+ // of its first sending days before its configured daily cap applies. A sending
3
+ // day is a completed UTC day on which the mailbox had an accepted campaign send
4
+ // (`campaignSendingDays`), so idle days and import age never advance it.
5
+ //
6
+ // Three modes, one stored column (`email_mailboxes.ramp_config`):
7
+ // - standard (null) 5 / 10 / 20 a day over the first nine sending days,
8
+ // then the configured cap.
9
+ // - custom start_per_day, plus `step` every `increase_every_days`
10
+ // sending days, until the configured cap. An optional
11
+ // `cap` is a permanent ceiling below the daily cap.
12
+ // - off ({enabled:false}) the configured cap from the first sending day, for
13
+ // an inbox that already has sending history elsewhere.
14
+ //
15
+ // Pure and browser-safe: the send claim, every API readback and the web preview
16
+ // compute the same numbers from these functions.
17
+ /**
18
+ * The standard ramp, by completed sending days: 5 a day through 2, 10 through 5,
19
+ * 20 through 8, then the configured cap. `maxAgeDays` keeps its historical name;
20
+ * its input is completed sending days, not mailbox age.
21
+ */
22
+ export const WARMUP_RAMP_DAILY_CAPS = [
23
+ { maxAgeDays: 2, cap: 5 },
24
+ { maxAgeDays: 5, cap: 10 },
25
+ { maxAgeDays: 8, cap: 20 },
26
+ ];
27
+ export const MAILBOX_RAMP_MODES = ["standard", "custom", "off"];
28
+ export function isMailboxRampOff(config) {
29
+ return Boolean(config) && config.enabled === false;
30
+ }
31
+ export function mailboxRampMode(config) {
32
+ if (!config)
33
+ return "standard";
34
+ return isMailboxRampOff(config) ? "off" : "custom";
35
+ }
36
+ /** The custom knobs, or null for the standard ramp and for a ramp switched off. */
37
+ export function customMailboxRamp(config) {
38
+ return config && !isMailboxRampOff(config) ? config : null;
39
+ }
40
+ const CUSTOM_REQUIRED_FIELDS = ["start_per_day", "increase_every_days", "step"];
41
+ function isNonNegativeInteger(value) {
42
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
43
+ }
44
+ /**
45
+ * The one validator for a ramp_config write, shared by the API route (400) and
46
+ * the tenant write (OxygenError). null/undefined selects the standard ramp.
47
+ * `{enabled: false}` turns the ramp off and takes no other field. Otherwise
48
+ * start_per_day, increase_every_days (>= 1) and step are required non-negative
49
+ * integers, and cap is an optional non-negative integer.
50
+ */
51
+ export function parseMailboxRampConfig(value) {
52
+ if (value === undefined || value === null)
53
+ return { ok: true, config: null };
54
+ if (typeof value !== "object" || Array.isArray(value)) {
55
+ return { ok: false, field: null, message: "ramp_config must be an object, or null for the standard ramp." };
56
+ }
57
+ const record = value;
58
+ if (record.enabled !== undefined && typeof record.enabled !== "boolean") {
59
+ return { ok: false, field: "enabled", message: "ramp_config.enabled must be true or false." };
60
+ }
61
+ if (record.enabled === false) {
62
+ const extra = Object.keys(record).filter((key) => key !== "enabled" && record[key] !== undefined);
63
+ if (extra.length > 0) {
64
+ return {
65
+ ok: false,
66
+ field: extra[0] ?? null,
67
+ message: "ramp_config {enabled: false} turns the ramp off; send it without other fields.",
68
+ };
69
+ }
70
+ return { ok: true, config: { enabled: false } };
71
+ }
72
+ const config = {};
73
+ for (const field of CUSTOM_REQUIRED_FIELDS) {
74
+ const raw = record[field];
75
+ if (!isNonNegativeInteger(raw)) {
76
+ return { ok: false, field, message: `ramp_config.${field} must be a non-negative integer.` };
77
+ }
78
+ config[field] = raw;
79
+ }
80
+ if (config.increase_every_days < 1) {
81
+ return { ok: false, field: "increase_every_days", message: "ramp_config.increase_every_days must be at least 1." };
82
+ }
83
+ if (record.cap !== undefined && record.cap !== null) {
84
+ if (!isNonNegativeInteger(record.cap)) {
85
+ return { ok: false, field: "cap", message: "ramp_config.cap must be a non-negative integer, or omitted to climb to the daily cap." };
86
+ }
87
+ config.cap = record.cap;
88
+ }
89
+ return { ok: true, config };
90
+ }
91
+ // Private persistence only. Previous workers require all four custom knobs and
92
+ // otherwise read a row as the standard ramp (which can exceed a new 0/1-day
93
+ // custom start). Keep them a bounded, valid ramp until they have rolled too.
94
+ const STORAGE_MARKER = "__oxygen_ramp_compat";
95
+ const STORED_FIELDS = new Set([...CUSTOM_REQUIRED_FIELDS, "cap", STORAGE_MARKER]);
96
+ /**
97
+ * Encode a validated public config for rolling web/worker deployments. The
98
+ * snapshot is the configured cap in force, taken under the mailbox's row lock.
99
+ * New readers discard it; it is never a permanent custom ceiling. Cap edits
100
+ * must refresh it, including Off's fixed start, so old warm-up targets cannot
101
+ * retain a higher value after the mailbox's daily cap is lowered.
102
+ */
103
+ export function serializeMailboxRampConfig(config, configuredDailyCap) {
104
+ if (!config)
105
+ return null;
106
+ const cap = wholeDailyCap(configuredDailyCap);
107
+ if (isMailboxRampOff(config)) {
108
+ return { start_per_day: cap, increase_every_days: 1, step: 0, cap, [STORAGE_MARKER]: "off-v1" };
109
+ }
110
+ if (config.cap !== undefined)
111
+ return { ...config };
112
+ return { ...config, cap, [STORAGE_MARKER]: "uncapped-v1" };
113
+ }
114
+ /** Non-throwing read of stored ramp_config; private compatibility fields never reach public readbacks. */
115
+ export function readMailboxRampConfig(value) {
116
+ if (value && typeof value === "object" && !Array.isArray(value)) {
117
+ const record = value;
118
+ if (Object.hasOwn(record, STORAGE_MARKER)) {
119
+ if (Object.keys(record).some((key) => !STORED_FIELDS.has(key)))
120
+ return null;
121
+ const legacy = parseMailboxRampConfig(record);
122
+ if (!legacy.ok || !legacy.config || isMailboxRampOff(legacy.config)
123
+ || !isNonNegativeInteger(record.cap))
124
+ return null;
125
+ if (record[STORAGE_MARKER] === "off-v1") {
126
+ return legacy.config.start_per_day === record.cap && legacy.config.step === 0
127
+ && legacy.config.increase_every_days === 1 ? { enabled: false } : null;
128
+ }
129
+ if (record[STORAGE_MARKER] !== "uncapped-v1")
130
+ return null;
131
+ const { cap: _snapshot, ...custom } = legacy.config;
132
+ return custom;
133
+ }
134
+ }
135
+ const parsed = parseMailboxRampConfig(value);
136
+ return parsed.ok ? parsed.config : null;
137
+ }
138
+ /** Refresh only a synthetic compatibility ceiling; explicit caps and malformed legacy rows stay intact. */
139
+ export function refreshMailboxRampConfigDailyCap(value, configuredDailyCap) {
140
+ const config = readMailboxRampConfig(value);
141
+ return config && (isMailboxRampOff(config) || config.cap === undefined)
142
+ ? serializeMailboxRampConfig(config, configuredDailyCap) : value;
143
+ }
144
+ function wholeSendingDays(sendingDays) {
145
+ return Number.isFinite(sendingDays) && sendingDays > 0 ? Math.floor(sendingDays) : 0;
146
+ }
147
+ /** The standard ramp's ceiling after `sendingDays` completed sending days; null once past its tiers. */
148
+ export function warmupRampDailyCap(sendingDays) {
149
+ const days = wholeSendingDays(sendingDays);
150
+ for (const tier of WARMUP_RAMP_DAILY_CAPS) {
151
+ if (days <= tier.maxAgeDays)
152
+ return tier.cap;
153
+ }
154
+ return null;
155
+ }
156
+ /**
157
+ * A custom ramp's ceiling after `sendingDays` completed sending days: start_per_day
158
+ * plus one `step` per completed `increase_every_days` window, held to `cap` when
159
+ * one is set. A (defensively) zero interval never climbs instead of dividing by zero.
160
+ */
161
+ export function rampConfigDailyCap(config, sendingDays) {
162
+ const days = wholeSendingDays(sendingDays);
163
+ const periods = config.increase_every_days >= 1 ? Math.floor(days / config.increase_every_days) : 0;
164
+ const value = config.start_per_day + config.step * periods;
165
+ const ceiling = typeof config.cap === "number" ? Math.min(value, config.cap) : value;
166
+ return Math.max(0, ceiling);
167
+ }
168
+ /** The campaign ramp's ceiling in any mode; null when the ramp does not hold the inbox back. */
169
+ export function campaignRampDailyCap(config, sendingDays) {
170
+ if (isMailboxRampOff(config))
171
+ return null;
172
+ const custom = customMailboxRamp(config);
173
+ return custom ? rampConfigDailyCap(custom, sendingDays) : warmupRampDailyCap(sendingDays);
174
+ }
175
+ /** Completed sending days at which the standard ramp changes level. */
176
+ const STANDARD_RAMP_BOUNDARIES = [0, ...WARMUP_RAMP_DAILY_CAPS.map((tier) => tier.maxAgeDays + 1)];
177
+ function wholeDailyCap(dailyCap) {
178
+ return Number.isFinite(dailyCap) && dailyCap > 0 ? Math.floor(dailyCap) : 0;
179
+ }
180
+ /**
181
+ * The 1-based sending day on which the ramp first lets the inbox send its full
182
+ * daily cap: 1 when the ramp is off or already at the cap, null when it never
183
+ * gets there (a custom cap below the daily cap, or no increase).
184
+ */
185
+ export function campaignRampFullCapSendingDay(config, dailyCap) {
186
+ const cap = wholeDailyCap(dailyCap);
187
+ if (cap === 0 || isMailboxRampOff(config))
188
+ return 1;
189
+ const custom = customMailboxRamp(config);
190
+ if (!custom) {
191
+ for (const sendingDays of STANDARD_RAMP_BOUNDARIES) {
192
+ const ramp = warmupRampDailyCap(sendingDays);
193
+ if (ramp === null || ramp >= cap)
194
+ return sendingDays + 1;
195
+ }
196
+ return null;
197
+ }
198
+ if (typeof custom.cap === "number" && custom.cap < cap)
199
+ return null;
200
+ if (custom.start_per_day >= cap)
201
+ return 1;
202
+ if (custom.step <= 0)
203
+ return null;
204
+ const periods = Math.ceil((cap - custom.start_per_day) / custom.step);
205
+ return periods * Math.max(1, custom.increase_every_days) + 1;
206
+ }
207
+ /**
208
+ * The day-by-day plan a ramp produces against a daily cap, as distinct levels.
209
+ * Standard against 25: 5 (day 1), 10 (day 4), 20 (day 7), 25 (day 10). A custom
210
+ * ramp whose own cap sits below the daily cap ends at that cap and never reports
211
+ * a full-cap day. `maxSteps` bounds a very gentle custom ramp (1 a day, +1 a day).
212
+ */
213
+ export function campaignRampSchedule(config, dailyCap, options = {}) {
214
+ const mode = mailboxRampMode(config);
215
+ const cap = wholeDailyCap(dailyCap);
216
+ const maxSteps = Math.max(2, Math.floor(options.maxSteps ?? 60));
217
+ const steps = [];
218
+ // Levels change only at a tier boundary (standard) or a period boundary
219
+ // (custom), so visiting those boundaries visits every distinct level.
220
+ const visit = (sendingDays) => {
221
+ const ramp = campaignRampDailyCap(config, sendingDays);
222
+ const perDay = ramp === null ? cap : Math.min(cap, ramp);
223
+ if (steps[steps.length - 1]?.perDay === perDay)
224
+ return;
225
+ steps.push({ fromSendingDay: sendingDays + 1, perDay });
226
+ };
227
+ const custom = customMailboxRamp(config);
228
+ let truncated = false;
229
+ if (mode === "off") {
230
+ visit(0);
231
+ }
232
+ else if (!custom) {
233
+ for (const sendingDays of STANDARD_RAMP_BOUNDARIES)
234
+ visit(sendingDays);
235
+ }
236
+ else {
237
+ const ceiling = typeof custom.cap === "number" ? Math.min(cap, custom.cap) : cap;
238
+ const every = Math.max(1, custom.increase_every_days);
239
+ for (let period = 0;; period += 1) {
240
+ visit(period * every);
241
+ if ((steps[steps.length - 1]?.perDay ?? 0) >= ceiling || custom.step <= 0)
242
+ break;
243
+ if (steps.length >= maxSteps) {
244
+ truncated = true;
245
+ break;
246
+ }
247
+ }
248
+ }
249
+ return { mode, steps, fullCapOnSendingDay: campaignRampFullCapSendingDay(config, cap), truncated };
250
+ }
@@ -16,12 +16,18 @@ export type McpReplyClassification = (typeof MCP_REPLY_CLASSIFICATIONS)[number];
16
16
  export declare const MCP_REPLY_CHANNELS: readonly ["email", "linkedin", "whatsapp"];
17
17
  /** How an enrollment ended: the Sequencer's own terminal statuses. */
18
18
  export declare const MCP_ENROLLMENT_OUTCOMES: readonly ["completed", "replied", "stopped", "failed", "excluded"];
19
- /** The run ledgers `run_outcome_analytics` reads (`SettledRunKind` in @oxygen/tenant-db). */
20
- export declare const MCP_RUN_KINDS: readonly ["workflow", "table_action", "table_ingestion", "agent", "copilot_turn"];
19
+ /**
20
+ * The durable-run ledgers `run_outcome_analytics` reads (`SettledRunKind` in
21
+ * @oxygen/tenant-db), without Workspace Copilot turns: a turn is attended and
22
+ * private to its member, the own-action guard already drops a subscriber's own
23
+ * turns, and delivering other members' turns would expose private activity
24
+ * with deep links the subscriber may not be able to open.
25
+ */
26
+ export declare const MCP_RUN_KINDS: readonly ["workflow", "table_action", "table_ingestion", "agent"];
21
27
  export type McpRunKind = (typeof MCP_RUN_KINDS)[number];
22
28
  /**
23
29
  * Every terminal status those ledgers write, with one spelling of cancel:
24
- * Workflow and Table runs write `canceled`, Agent runs and Copilot turns `cancelled`.
30
+ * Workflow and Table runs write `canceled`, Agent runs `cancelled`.
25
31
  */
26
32
  export declare const MCP_RUN_STATUSES: readonly ["completed", "completed_with_errors", "failed", "canceled", "budget_exhausted", "effect_unknown"];
27
33
  export type McpRunStatus = (typeof MCP_RUN_STATUSES)[number];
@@ -285,7 +291,7 @@ declare const RECORD_STAGE_CHANGED_PAYLOAD: ContractObject<{
285
291
  declare const RUN_SETTLED_PAYLOAD: ContractObject<{
286
292
  readonly kind: {
287
293
  type: "string";
288
- enum: readonly ("agent" | "copilot_turn" | "table_action" | "table_ingestion" | "workflow")[];
294
+ enum: readonly ("agent" | "table_action" | "table_ingestion" | "workflow")[];
289
295
  description?: string;
290
296
  };
291
297
  readonly status: {
@@ -574,9 +580,6 @@ export type McpRunSettledSource = McpRunSettledCommon & ({
574
580
  } | {
575
581
  kind: "agent";
576
582
  agentSlug: string;
577
- } | {
578
- kind: "copilot_turn";
579
- copilotSessionId: string;
580
583
  });
581
584
  /** A settled run as `run.settled`. Its producer is OXP-11.5.21. */
582
585
  export declare function projectRunSettledEvent(source: McpRunSettledSource, context?: McpEventLinkOptions): McpRunSettledPayload;
@@ -59,11 +59,17 @@ const REPLY_LABELS_DELIVERED_AS_IS = new Set([
59
59
  export const MCP_REPLY_CHANNELS = ["email", "linkedin", "whatsapp"];
60
60
  /** How an enrollment ended: the Sequencer's own terminal statuses. */
61
61
  export const MCP_ENROLLMENT_OUTCOMES = SEQUENCE_TERMINAL_STATUSES;
62
- /** The run ledgers `run_outcome_analytics` reads (`SettledRunKind` in @oxygen/tenant-db). */
63
- export const MCP_RUN_KINDS = ["workflow", "table_action", "table_ingestion", "agent", "copilot_turn"];
62
+ /**
63
+ * The durable-run ledgers `run_outcome_analytics` reads (`SettledRunKind` in
64
+ * @oxygen/tenant-db), without Workspace Copilot turns: a turn is attended and
65
+ * private to its member, the own-action guard already drops a subscriber's own
66
+ * turns, and delivering other members' turns would expose private activity
67
+ * with deep links the subscriber may not be able to open.
68
+ */
69
+ export const MCP_RUN_KINDS = ["workflow", "table_action", "table_ingestion", "agent"];
64
70
  /**
65
71
  * Every terminal status those ledgers write, with one spelling of cancel:
66
- * Workflow and Table runs write `canceled`, Agent runs and Copilot turns `cancelled`.
72
+ * Workflow and Table runs write `canceled`, Agent runs `cancelled`.
67
73
  */
68
74
  export const MCP_RUN_STATUSES = [
69
75
  "completed",
@@ -160,7 +166,7 @@ const RUN_SETTLED_INPUT = obj({
160
166
  });
161
167
  const RUN_SETTLED_PAYLOAD = payloadObject({
162
168
  kind: enumOf(MCP_RUN_KINDS, {
163
- description: "What ran: a Workflow run, a Table action run, a Table import (`table_ingestion`), an Agent run or a Workspace Copilot turn.",
169
+ description: "What ran: a Workflow run, a Table action run, a Table import (`table_ingestion`) or an Agent run.",
164
170
  }),
165
171
  status: enumOf(MCP_RUN_STATUSES, {
166
172
  description: "How it ended. An Agent run can also end `budget_exhausted` or `effect_unknown`.",
@@ -189,7 +195,7 @@ export const MCP_EVENT_DEFINITIONS = [
189
195
  eventDefinition("enrollment.ended", "A lead's enrollment in an OXYGEN Sequence ended: the lead replied, every step was sent, the enrollment was stopped or excluded, or sending failed.", ENROLLMENT_ENDED_INPUT, ENROLLMENT_ENDED_PAYLOAD),
190
196
  eventDefinition("meeting.completed", "OXYGEN's Meeting Notetaker finished processing a recorded meeting; its transcript and meeting note are ready in OXYGEN.", MEETING_COMPLETED_INPUT, MEETING_COMPLETED_PAYLOAD),
191
197
  eventDefinition("record.stage_changed", "A CRM record's stage changed in OXYGEN, such as a person's lead stage, a company's account stage or a deal's pipeline stage.", RECORD_STAGE_CHANGED_INPUT, RECORD_STAGE_CHANGED_PAYLOAD),
192
- eventDefinition("run.settled", "A run in OXYGEN reached its final status: a Workflow run, a Table action run or import, an Agent run, or a Workspace Copilot turn.", RUN_SETTLED_INPUT, RUN_SETTLED_PAYLOAD),
198
+ eventDefinition("run.settled", "A run in OXYGEN reached its final status: a Workflow run, a Table action run or import, or an Agent run.", RUN_SETTLED_INPUT, RUN_SETTLED_PAYLOAD),
193
199
  eventDefinition("approval.requested", "A Workflow run or an Agent run in OXYGEN paused and is waiting for a person to approve its next step.", APPROVAL_REQUESTED_INPUT, APPROVAL_REQUESTED_PAYLOAD),
194
200
  ];
195
201
  const DEFINITIONS_BY_NAME = new Map(MCP_EVENT_DEFINITIONS.map((entry) => [entry.name, entry]));
@@ -574,8 +580,6 @@ function runPath(source) {
574
580
  return `/tables/${encodeURIComponent(source.tableId)}/ingestions/${encodeURIComponent(source.runId)}`;
575
581
  case "agent":
576
582
  return agentRunPath(source.agentSlug, source.runId);
577
- case "copilot_turn":
578
- return `/copilot/${encodeURIComponent(source.copilotSessionId)}`;
579
583
  }
580
584
  }
581
585
  // Every catalogue projection first proves the row is that event, so a reader
@@ -0,0 +1,50 @@
1
+ export declare const TABLE_WIDGET_NAME = "oxygen.table";
2
+ /** A run filling the Table, shown as progress until it reaches a terminal state. */
3
+ export type TableWidgetRun = {
4
+ kind: "ingestion" | "column_run";
5
+ id: string;
6
+ status: string;
7
+ terminal: boolean;
8
+ /** Settled / total work items in this run (chunks for an ingestion), not Table rows. */
9
+ rowsDone?: number | null;
10
+ rowsTarget?: number | null;
11
+ creditsUsed?: number | null;
12
+ creditsCap?: number | null;
13
+ };
14
+ export type TableWidgetGridPage = {
15
+ /** SerializedWorkspaceColumn[] from the web grid serializer. */
16
+ columns: unknown[];
17
+ /** SerializedWorkspaceTableRow[] from the web grid serializer. */
18
+ rows: unknown[];
19
+ cellStates?: Record<string, Record<string, unknown>> | null;
20
+ rowCount?: number | null;
21
+ hasMore?: boolean;
22
+ nextCursor?: string | null;
23
+ /**
24
+ * SerializedWorkspaceColumnStats[], the grid's column stats: what it
25
+ * collapses as empty, its header fill lines and run bars. Only a first page
26
+ * carries them, and only when they answered in time.
27
+ */
28
+ columnStats?: unknown[] | null;
29
+ /**
30
+ * An empty first page's fill state, the body of GET
31
+ * /api/tables/<id>/fill-progress: rows still being written, a failed fill,
32
+ * or neither.
33
+ */
34
+ fillProgress?: Record<string, unknown> | null;
35
+ };
36
+ export type TableWidgetPayload = {
37
+ version: 1;
38
+ /** "table": rows of a saved Table. "preview": sample rows of a search that saved nothing yet. */
39
+ source: "table" | "preview";
40
+ title: string;
41
+ /** The counts line under the title, e.g. "1,840 matches · 0 credits". */
42
+ summary?: string | null;
43
+ /** The Table these rows belong to; absent for a preview. Paging and live progress read it. */
44
+ tableId?: string | null;
45
+ webUrl?: string | null;
46
+ grid: TableWidgetGridPage;
47
+ run?: TableWidgetRun | null;
48
+ };
49
+ /** The payload inside a tool result, or null when the result carries none (a refusal, a filter list). */
50
+ export declare function readTableWidgetPayload(result: unknown): TableWidgetPayload | null;
@@ -0,0 +1,24 @@
1
+ // What an MCP tool hands the OXYGEN Table widget. The tool puts it in its
2
+ // result's `_meta["oxygen/widget"].data`; MCP Apps hosts pass result `_meta` to
3
+ // the widget and not to the model, so the grid page the widget draws costs the
4
+ // model no context. The widget renders the page with the web grid's own
5
+ // components, so `grid` carries the web grid's serialization unchanged
6
+ // (columns, rows, cell states as GET /api/tables/<id>/rows returns them).
7
+ export const TABLE_WIDGET_NAME = "oxygen.table";
8
+ function isRecord(value) {
9
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10
+ }
11
+ /** The payload inside a tool result, or null when the result carries none (a refusal, a filter list). */
12
+ export function readTableWidgetPayload(result) {
13
+ if (!isRecord(result) || !isRecord(result._meta))
14
+ return null;
15
+ const widget = result._meta["oxygen/widget"];
16
+ if (!isRecord(widget) || widget.name !== TABLE_WIDGET_NAME || !isRecord(widget.data))
17
+ return null;
18
+ const data = widget.data;
19
+ if (data.version !== 1 || typeof data.title !== "string" || !isRecord(data.grid))
20
+ return null;
21
+ if (!Array.isArray(data.grid.columns) || !Array.isArray(data.grid.rows))
22
+ return null;
23
+ return data;
24
+ }
@@ -84,6 +84,14 @@ export type PlanSpendResolution = {
84
84
  * is never clamped by this.
85
85
  */
86
86
  export declare function resolveDefaultTriggerRunCreditCeilingForPlan(resolution: PlanSpendResolution, options?: RepricingOptions): number | null;
87
+ /**
88
+ * The default rolling 24-hour credit ceiling of one connected app (an MCP OAuth
89
+ * connection such as ChatGPT) while it holds MCP event subscriptions (ADR 0030
90
+ * §11, decision D3): the trigger default above, so the two cannot drift.
91
+ * Enterprise has no trigger default and uses the largest rung's, the way Agent
92
+ * run defaults do; a connection is never uncapped by default.
93
+ */
94
+ export declare function resolveDefaultConnectionCreditCeilingForPlan(resolution: PlanSpendResolution, options?: RepricingOptions): number;
87
95
  /** The same default for one standing or webhook table auto-run batch. */
88
96
  export declare function resolveDefaultAutoRunBatchCreditCeilingForPlan(resolution: PlanSpendResolution, options?: RepricingOptions): number | null;
89
97
  /**
@@ -183,6 +183,18 @@ function resolveDeliveryCreditCeiling(resolution, preRepricingTable, options) {
183
183
  export function resolveDefaultTriggerRunCreditCeilingForPlan(resolution, options = {}) {
184
184
  return resolveDeliveryCreditCeiling(resolution, DEFAULT_TRIGGER_RUN_CREDIT_CEILING, options);
185
185
  }
186
+ /**
187
+ * The default rolling 24-hour credit ceiling of one connected app (an MCP OAuth
188
+ * connection such as ChatGPT) while it holds MCP event subscriptions (ADR 0030
189
+ * §11, decision D3): the trigger default above, so the two cannot drift.
190
+ * Enterprise has no trigger default and uses the largest rung's, the way Agent
191
+ * run defaults do; a connection is never uncapped by default.
192
+ */
193
+ export function resolveDefaultConnectionCreditCeilingForPlan(resolution, options = {}) {
194
+ return resolveDefaultTriggerRunCreditCeilingForPlan(resolution, options)
195
+ ?? resolveDefaultTriggerRunCreditCeilingForPlan(bandSpendResolution("1999"), options)
196
+ ?? 0;
197
+ }
186
198
  /** The same default for one standing or webhook table auto-run batch. */
187
199
  export function resolveDefaultAutoRunBatchCreditCeilingForPlan(resolution, options = {}) {
188
200
  return resolveDeliveryCreditCeiling(resolution, DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING, options);
@@ -1,4 +1,5 @@
1
1
  import { type RepricingOptions } from "./repricing.js";
2
+ export { CONNECTION_CEILING_ABOVE_DEFAULT_APPROVAL_KIND } from "./connection-approval.js";
2
3
  /**
3
4
  * The five enforcement rungs of the per-plan limits ladder. Every PlanTier maps
4
5
  * onto exactly one rung: "free" doubles as the unentitled fallback (no live
@@ -844,6 +845,25 @@ export declare function assertAgentRunCeilingApproval(input: {
844
845
  }): {
845
846
  aboveDefault: boolean;
846
847
  };
848
+ /**
849
+ * The same approval posture for a connected app's rolling 24-hour credit
850
+ * ceiling (ADR 0030 §11): at or under the default (the plan-sized trigger
851
+ * default) nothing more to approve; above it only with the explicit
852
+ * above-default approval, whose flag and refusal shape match the Agent run and
853
+ * trigger ceilings; above the platform maximum never. `defaultCredits` is the
854
+ * lowest default among the workspaces the app can spend in, so the approval
855
+ * covers every one of them.
856
+ */
857
+ export declare function assertConnectionCreditCeilingApproval(input: {
858
+ creditCeiling: number;
859
+ defaultCredits: number;
860
+ approveAboveDefault: boolean;
861
+ appName: string;
862
+ /** The workspace whose default is the one compared, named in the refusal. */
863
+ workspaceName?: string;
864
+ }): {
865
+ aboveDefault: boolean;
866
+ };
847
867
  /**
848
868
  * The ceilings one run is held to, for the worker and every budget check that
849
869
  * reads a run's authority. A run with a recorded above-default approval is