@oxygen-agent/cli 1.1011.855 → 1.1011.1091

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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/admin-lead-magnet-commands.d.ts +7 -0
  3. package/dist/admin-lead-magnet-commands.js +128 -0
  4. package/dist/command-manifest.js +29 -0
  5. package/dist/crm-support-flags.d.ts +1 -0
  6. package/dist/crm-support-flags.js +5 -1
  7. package/dist/customer-operation-commands.js +30 -1
  8. package/dist/functions-commands.js +1 -1
  9. package/dist/help.js +10 -2
  10. package/dist/http-client.d.ts +4 -0
  11. package/dist/http-client.js +21 -10
  12. package/dist/index.js +444 -208
  13. package/dist/sender-limits-flags.d.ts +7 -0
  14. package/dist/sender-limits-flags.js +13 -0
  15. package/dist/workspace-utility-flags.d.ts +7 -0
  16. package/dist/workspace-utility-flags.js +45 -0
  17. package/node_modules/@oxygen/shared/dist/attempt-provenance.d.ts +22 -0
  18. package/node_modules/@oxygen/shared/dist/attempt-provenance.js +83 -0
  19. package/node_modules/@oxygen/shared/dist/billing-anchors.d.ts +6 -0
  20. package/node_modules/@oxygen/shared/dist/billing-anchors.js +7 -1
  21. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +6 -0
  22. package/node_modules/@oxygen/shared/dist/capability-discovery.js +279 -13
  23. package/node_modules/@oxygen/shared/dist/cli-result.js +6 -0
  24. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +21 -9
  25. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.d.ts +7 -1
  26. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.js +5 -1
  27. package/node_modules/@oxygen/shared/dist/contracts/crm-support.d.ts +14 -0
  28. package/node_modules/@oxygen/shared/dist/contracts/crm-support.js +4 -0
  29. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +13 -11
  30. package/node_modules/@oxygen/shared/dist/contracts/limits-read.js +11 -1
  31. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +6 -0
  32. package/node_modules/@oxygen/shared/dist/contracts/publishing-reads.js +1 -0
  33. package/node_modules/@oxygen/shared/dist/contracts/schema.js +10 -6
  34. package/node_modules/@oxygen/shared/dist/contracts/spintax.d.ts +18 -0
  35. package/node_modules/@oxygen/shared/dist/contracts/spintax.js +11 -0
  36. package/node_modules/@oxygen/shared/dist/contracts/table-row-removal.d.ts +8 -0
  37. package/node_modules/@oxygen/shared/dist/contracts/table-row-removal.js +21 -0
  38. package/node_modules/@oxygen/shared/dist/contracts/workspace-utilities.d.ts +162 -0
  39. package/node_modules/@oxygen/shared/dist/contracts/workspace-utilities.js +46 -0
  40. package/node_modules/@oxygen/shared/dist/credit-topup-review.d.ts +11 -0
  41. package/node_modules/@oxygen/shared/dist/credit-topup-review.js +3 -0
  42. package/node_modules/@oxygen/shared/dist/emailguard-placement-probe.d.ts +70 -0
  43. package/node_modules/@oxygen/shared/dist/emailguard-placement-probe.js +161 -0
  44. package/node_modules/@oxygen/shared/dist/future-signup-events.d.ts +1 -1
  45. package/node_modules/@oxygen/shared/dist/future-signup-events.js +4 -0
  46. package/node_modules/@oxygen/shared/dist/knowledge-seed-content.js +2 -2
  47. package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.d.ts +48 -0
  48. package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.js +94 -0
  49. package/node_modules/@oxygen/shared/dist/partner-coupon-bonus.d.ts +45 -0
  50. package/node_modules/@oxygen/shared/dist/partner-coupon-bonus.js +57 -0
  51. package/node_modules/@oxygen/shared/dist/provider-balance-signal.js +3 -2
  52. package/node_modules/@oxygen/shared/dist/table-filter-operators.d.ts +6 -0
  53. package/node_modules/@oxygen/shared/dist/table-filter-operators.js +50 -0
  54. package/node_modules/@oxygen/shared/dist/telemetry.js +4 -0
  55. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  56. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  57. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  58. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  59. package/node_modules/@oxygen/shared/dist/whatsapp-sender-limits.d.ts +42 -0
  60. package/node_modules/@oxygen/shared/dist/whatsapp-sender-limits.js +68 -0
  61. package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.d.ts +15 -0
  62. package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.js +33 -0
  63. package/node_modules/@oxygen/shared/package.json +5 -5
  64. package/node_modules/@oxygen/workflows/dist/index.d.ts +1 -0
  65. package/node_modules/@oxygen/workflows/dist/index.js +1 -0
  66. package/node_modules/@oxygen/workflows/dist/recipe-timestamp.d.ts +22 -0
  67. package/node_modules/@oxygen/workflows/dist/recipe-timestamp.js +72 -0
  68. package/node_modules/@oxygen/workflows/dist/usage-estimate.js +9 -5
  69. package/package.json +1 -1
  70. package/node_modules/@oxygen/shared/dist/vercel-sandbox-fetch.d.ts +0 -9
  71. package/node_modules/@oxygen/shared/dist/vercel-sandbox-fetch.js +0 -33
@@ -1,4 +1,11 @@
1
+ import type { Command } from "commander";
1
2
  import { type ContractFlagOverlay } from "./contract-flags.js";
3
+ /**
4
+ * `whatsapp limits set`: one flag per key the WhatsApp limits route keeps, with
5
+ * the WhatsApp defaults and maximums from the shared owner (OXP-11.3.19). The
6
+ * body is still built by buildSenderLimitsBody, which reads the same flag names.
7
+ */
8
+ export declare function addWhatsAppLimitFlags(command: Command): Command;
2
9
  /**
3
10
  * `--working-days` → ISO weekday numbers. Accepts day names or 1-7, comma
4
11
  * separated, and ranges (`mon-fri`, `1-5`). A token it cannot read is sent as
@@ -5,6 +5,7 @@
5
5
  import { OxygenError } from "@oxygen/shared/cli-result";
6
6
  import { LINKEDIN_SENDER_LIMIT_DEFAULTS, LINKEDIN_SENDER_LIMIT_MAXIMUMS, linkedInSenderLimitCautionLevel, } from "@oxygen/shared/linkedin-sequences";
7
7
  import { SENDER_LIMITS_PATCH_CONTRACT } from "@oxygen/shared/contracts/sender-limits";
8
+ import { WHATSAPP_SENDER_LIMIT_DEFAULTS, WHATSAPP_SENDER_LIMIT_KEYS, WHATSAPP_SENDER_LIMIT_MAXIMUMS, WHATSAPP_SENDER_LIMIT_NOTES, } from "@oxygen/shared/whatsapp-sender-limits";
8
9
  import { readContractFlags } from "./contract-flags.js";
9
10
  /** "<text> (default N, max M)." for a LinkedIn sender limit flag, from the enforced constants. */
10
11
  function linkedInLimitHelp(key, text) {
@@ -48,6 +49,18 @@ const LIMIT_FLAG_NAMES = {
48
49
  min_action_spacing_seconds: "min-spacing-seconds",
49
50
  action_spacing_jitter_seconds: "spacing-jitter-seconds",
50
51
  };
52
+ /**
53
+ * `whatsapp limits set`: one flag per key the WhatsApp limits route keeps, with
54
+ * the WhatsApp defaults and maximums from the shared owner (OXP-11.3.19). The
55
+ * body is still built by buildSenderLimitsBody, which reads the same flag names.
56
+ */
57
+ export function addWhatsAppLimitFlags(command) {
58
+ for (const key of WHATSAPP_SENDER_LIMIT_KEYS) {
59
+ const flag = LIMIT_FLAG_NAMES[key] ?? key.replace(/_/g, "-");
60
+ command.option(`--${flag} <n>`, `${WHATSAPP_SENDER_LIMIT_NOTES[key]} (default ${WHATSAPP_SENDER_LIMIT_DEFAULTS[key]}, max ${WHATSAPP_SENDER_LIMIT_MAXIMUMS[key]}; higher values are clamped).`);
61
+ }
62
+ return command;
63
+ }
51
64
  const WORKING_DAY_NAMES = {
52
65
  mon: 1, monday: 1, tue: 2, tues: 2, tuesday: 2, wed: 3, wednesday: 3, thu: 4, thur: 4, thurs: 4, thursday: 4,
53
66
  fri: 5, friday: 5, sat: 6, saturday: 6, sun: 7, sunday: 7,
@@ -0,0 +1,7 @@
1
+ import type { Command } from "commander";
2
+ /** Existing native leaves retain their names and default-list alias. */
3
+ export declare function addProviderLimitFlags(command: Command, operation: "list" | "set" | "delete"): Command;
4
+ export declare function providerLimitBody(options: object): Record<string, unknown>;
5
+ export declare function providerLimitQuery(options: object, operation: "list" | "delete"): string;
6
+ export declare function addSuppressionListFlags(command: Command, kind: "addresses" | "domains"): Command;
7
+ export declare function suppressionListQuery(options: object, kind: "addresses" | "domains"): string;
@@ -0,0 +1,45 @@
1
+ import { OxygenError } from "@oxygen/shared/cli-result";
2
+ import { validateContract } from "@oxygen/shared/contracts/schema";
3
+ import { emailDomainSuppressionListContract, emailSuppressionListContract, providerLimitListContract, providerLimitSelectorContract, providerLimitUpdateContract } from "@oxygen/shared/contracts/workspace-utilities";
4
+ import { addContractFlags, readContractFlags } from "./contract-flags.js";
5
+ /** Existing native leaves retain their names and default-list alias. */
6
+ export function addProviderLimitFlags(command, operation) {
7
+ const contract = operation === "set" ? providerLimitUpdateContract : operation === "delete" ? providerLimitSelectorContract : providerLimitListContract;
8
+ addContractFlags(command, contract, {
9
+ provider: { valueName: "<provider>" }, operation: { valueName: "<operation>" },
10
+ limit: { valueName: "<n>" }, window_seconds: { valueName: "<n>" }, offset: { valueName: "<n>" }, max_chars: { valueName: "<n>" },
11
+ });
12
+ for (const field of contract.required ?? [])
13
+ command.options.find((option) => option.long === `--${field.replace(/_/g, "-")}`)?.makeOptionMandatory();
14
+ return command;
15
+ }
16
+ export function providerLimitBody(options) {
17
+ const body = readContractFlags(options, providerLimitUpdateContract);
18
+ const parsed = validateContract(providerLimitUpdateContract, body, { unknown: "reject", overMax: "reject" });
19
+ if (!parsed.ok)
20
+ throw new OxygenError("invalid_request", parsed.issues.map((issue) => issue.message).join(" "), { exitCode: 2 });
21
+ return body;
22
+ }
23
+ export function providerLimitQuery(options, operation) {
24
+ return query(input(options, operation === "list" ? providerLimitListContract : providerLimitSelectorContract));
25
+ }
26
+ export function addSuppressionListFlags(command, kind) {
27
+ return addContractFlags(command, suppressionContract(kind), {
28
+ reason: { valueName: "<reason>" }, search: { valueName: "<text>" }, limit: { valueName: "<n>" }, offset: { valueName: "<n>" }, max_chars: { valueName: "<n>" },
29
+ });
30
+ }
31
+ export function suppressionListQuery(options, kind) {
32
+ return query(input(options, suppressionContract(kind)));
33
+ }
34
+ function suppressionContract(kind) {
35
+ return kind === "addresses" ? emailSuppressionListContract : emailDomainSuppressionListContract;
36
+ }
37
+ function input(options, contract) {
38
+ const parsed = validateContract(contract, readContractFlags(options, contract), { unknown: "reject", overMax: "clamp" });
39
+ if (!parsed.ok)
40
+ throw new OxygenError("invalid_request", parsed.issues.map((issue) => issue.message).join(" "), { exitCode: 2 });
41
+ return parsed.value;
42
+ }
43
+ function query(args) {
44
+ return new URLSearchParams(Object.entries(args).filter(([, value]) => value !== null).map(([key, value]) => [key, String(value)])).toString();
45
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The identity a request asked about versus the one a provider answered with.
3
+ * Present on a refused attempt; `matched` is `false` whenever it is recorded.
4
+ */
5
+ export type AttemptIdentityMatch = {
6
+ matched?: boolean;
7
+ requested?: string | null;
8
+ returned?: string | null;
9
+ };
10
+ /** Label for a skipped attempt's reason. Unknown reasons stay generic. */
11
+ export declare function describeSkipReason(reason: string): string;
12
+ /**
13
+ * Label for a `no_result` attempt whose answer was refused rather than absent,
14
+ * or `null` for an ordinary empty answer (which still reads "No result").
15
+ */
16
+ export declare function describeRejectedAnswer(reason: string | null | undefined): string | null;
17
+ /**
18
+ * One-line requested-vs-returned evidence for a refused attempt, for the cell's
19
+ * hover title. `null` when neither side named an identity — there is nothing to
20
+ * show and an empty tooltip is worse than none.
21
+ */
22
+ export declare function describeIdentityMatch(identityMatch: AttemptIdentityMatch | null | undefined): string | null;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Human labels for a waterfall attempt's recorded `reason`. Single source of
3
+ * truth for the /tables data grid (`tables-workspace.tsx`) and the MCP
4
+ * enrichment-cell-detail widget, so an attempt explains itself one way wherever
5
+ * it is shown (OXP-7.3.40).
6
+ *
7
+ * Every label is grid-cell sized — these render inside a truncated attempt cell,
8
+ * not a detail panel.
9
+ */
10
+ const ATTEMPT_REASON_LABELS = {
11
+ missing_input: "Run condition not met",
12
+ // The company waterfall's spelling of `missing_input`: it needs at least one
13
+ // of domain / name / linkedin_url before any lane can be dispatched.
14
+ missing_identity_input: "Missing identity",
15
+ byok_not_connected: "Not connected",
16
+ byok_requires_paid_plan: "Plan required",
17
+ step_disabled: "Step disabled",
18
+ unsupported_credential_mode: "Mode unsupported",
19
+ unknown_capability: "Unknown provider",
20
+ // A provider answered about a DIFFERENT company than the request named. The
21
+ // answer was refused rather than written to the row (Plain T-111), so calling
22
+ // it "skipped" would describe the wrong event entirely.
23
+ identity_mismatch: "Wrong company",
24
+ // A search-shaped lane returned no identity at all, so its answer could not be
25
+ // checked against the requested company and was not trusted.
26
+ identity_unverifiable: "Identity unverified",
27
+ // A pre-step that ran only to resolve an identity (e.g. a company LinkedIn
28
+ // URL) a later requested field needs. It owns no target column and writes no
29
+ // cell — recorded so the extra provider call is visible, never silent.
30
+ identity_prerequisite: "Identity step",
31
+ // Bring-your-own-key lane with no usable connection for this workspace:
32
+ // not connected, unhealthy, missing secret, or plan-gated.
33
+ provider_credentials_unavailable: "No credentials",
34
+ // Priced above the run's approved credit ceiling, so it was never dispatched.
35
+ credit_ceiling_reached: "Credit cap reached",
36
+ // An identity pre-step lane the run stood down: the ceiling had no headroom
37
+ // above what the requested fields themselves need, so resolving the identity
38
+ // would have spent the answer-bearing lane's budget.
39
+ identity_prerequisite_unaffordable: "Identity step unfunded",
40
+ // Oxygen could not resolve a managed price for the lane, so it never ran.
41
+ managed_price_unavailable: "Price unavailable",
42
+ // The row's person LinkedIn input is a company or school page, or no profile
43
+ // link at all, so the row was refused before any lane ran.
44
+ linkedin_url_not_a_profile: "Not a profile URL",
45
+ };
46
+ /**
47
+ * Reasons that mean "we got an answer and refused it", not "there was nothing
48
+ * to find". They arrive on an attempt whose status is `no_result`, where the
49
+ * bare "No result" reads as an absent answer and hides the rejection.
50
+ */
51
+ const REJECTED_ANSWER_REASONS = new Set([
52
+ "identity_mismatch",
53
+ "identity_unverifiable",
54
+ ]);
55
+ /** Label for a skipped attempt's reason. Unknown reasons stay generic. */
56
+ export function describeSkipReason(reason) {
57
+ return ATTEMPT_REASON_LABELS[reason] ?? "Skipped";
58
+ }
59
+ /**
60
+ * Label for a `no_result` attempt whose answer was refused rather than absent,
61
+ * or `null` for an ordinary empty answer (which still reads "No result").
62
+ */
63
+ export function describeRejectedAnswer(reason) {
64
+ if (!reason || !REJECTED_ANSWER_REASONS.has(reason))
65
+ return null;
66
+ return ATTEMPT_REASON_LABELS[reason] ?? null;
67
+ }
68
+ /**
69
+ * One-line requested-vs-returned evidence for a refused attempt, for the cell's
70
+ * hover title. `null` when neither side named an identity — there is nothing to
71
+ * show and an empty tooltip is worse than none.
72
+ */
73
+ export function describeIdentityMatch(identityMatch) {
74
+ if (!identityMatch)
75
+ return null;
76
+ const requested = typeof identityMatch.requested === "string" ? identityMatch.requested.trim() : "";
77
+ const returned = typeof identityMatch.returned === "string" ? identityMatch.returned.trim() : "";
78
+ if (!requested && !returned)
79
+ return null;
80
+ const asked = requested ? `Requested ${requested}` : "Requested company";
81
+ const got = returned ? `provider returned ${returned}` : "provider returned no identity";
82
+ return `${asked}; ${got}`;
83
+ }
@@ -50,6 +50,12 @@ export declare function occurrencesInPeriod(from: Date, to: Date, anchorDay: num
50
50
  * not such a period: it grants monthly slices, so its caller passes the current
51
51
  * slice's length (see recomputeOrgCommitmentBlock, P-23).
52
52
  *
53
+ * A calendar month runs 28-31 days, so months are counted in 31-day steps: every
54
+ * monthly period holds one month, as OXYGEN_PRODUCT.md states ("one more month of
55
+ * every fixed resource"). Counting in 30-day steps held two months through every
56
+ * 31-day period, refusing customers' own credits as committed (staff decision
57
+ * 2026-10-06).
58
+ *
53
59
  * Returns at least 1 — a missing/zero/garbage period length must never collapse
54
60
  * the block to nothing, which would silently disable the whole feature.
55
61
  */
@@ -113,6 +113,12 @@ export function occurrencesInPeriod(from, to, anchorDay) {
113
113
  * not such a period: it grants monthly slices, so its caller passes the current
114
114
  * slice's length (see recomputeOrgCommitmentBlock, P-23).
115
115
  *
116
+ * A calendar month runs 28-31 days, so months are counted in 31-day steps: every
117
+ * monthly period holds one month, as OXYGEN_PRODUCT.md states ("one more month of
118
+ * every fixed resource"). Counting in 30-day steps held two months through every
119
+ * 31-day period, refusing customers' own credits as committed (staff decision
120
+ * 2026-10-06).
121
+ *
116
122
  * Returns at least 1 — a missing/zero/garbage period length must never collapse
117
123
  * the block to nothing, which would silently disable the whole feature.
118
124
  */
@@ -120,7 +126,7 @@ export function blockPeriodsForSubscription(periodDays) {
120
126
  if (typeof periodDays !== "number" || !Number.isFinite(periodDays) || periodDays <= 0) {
121
127
  return 1;
122
128
  }
123
- return Math.max(1, Math.ceil(periodDays / 30));
129
+ return Math.max(1, Math.ceil(periodDays / 31));
124
130
  }
125
131
  /**
126
132
  * Days between two instants, floored. Small helper so callers deriving
@@ -119,6 +119,12 @@ export declare const SEQUENCE_UNSUBSCRIBE_COMMAND = "oxygen sequences update <se
119
119
  * Factories, so a caller can edit the object it gets.
120
120
  */
121
121
  export declare const CAPABILITY_ROUTE_MCP_CALLS: {
122
+ connectionPrices: () => {
123
+ tool: string;
124
+ arguments: {
125
+ view: string;
126
+ };
127
+ };
122
128
  connectedAppsList: () => {
123
129
  tool: string;
124
130
  arguments: {};