@oxygen-agent/cli 1.1011.974 → 1.1012.2

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 (72) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.d.ts +3 -1
  3. package/dist/command-manifest.js +29 -0
  4. package/dist/crm-support-flags.d.ts +1 -0
  5. package/dist/crm-support-flags.js +5 -1
  6. package/dist/customer-operation-commands.js +30 -1
  7. package/dist/help.js +10 -2
  8. package/dist/http-client.d.ts +4 -0
  9. package/dist/http-client.js +21 -10
  10. package/dist/index.js +326 -111
  11. package/dist/sender-limits-flags.d.ts +7 -0
  12. package/dist/sender-limits-flags.js +13 -0
  13. package/dist/transcript.js +1 -1
  14. package/node_modules/@oxygen/formula/dist/expression.js +16 -0
  15. package/node_modules/@oxygen/shared/dist/attempt-provenance.d.ts +30 -0
  16. package/node_modules/@oxygen/shared/dist/attempt-provenance.js +98 -0
  17. package/node_modules/@oxygen/shared/dist/byok-connect.js +2 -0
  18. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +8 -1
  19. package/node_modules/@oxygen/shared/dist/capability-discovery.js +172 -13
  20. package/node_modules/@oxygen/shared/dist/cli-result.js +6 -0
  21. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +19 -7
  22. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.d.ts +7 -1
  23. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.js +5 -1
  24. package/node_modules/@oxygen/shared/dist/contracts/crm-support.d.ts +14 -0
  25. package/node_modules/@oxygen/shared/dist/contracts/crm-support.js +4 -0
  26. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +5 -5
  27. package/node_modules/@oxygen/shared/dist/contracts/geo.d.ts +2 -0
  28. package/node_modules/@oxygen/shared/dist/contracts/geo.js +138 -0
  29. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +6 -0
  30. package/node_modules/@oxygen/shared/dist/contracts/operations.js +4 -3
  31. package/node_modules/@oxygen/shared/dist/contracts/publishing-reads.js +1 -0
  32. package/node_modules/@oxygen/shared/dist/contracts/table-row-removal.d.ts +16 -0
  33. package/node_modules/@oxygen/shared/dist/contracts/table-row-removal.js +27 -0
  34. package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +6 -0
  35. package/node_modules/@oxygen/shared/dist/cost-estimate.js +23 -8
  36. package/node_modules/@oxygen/shared/dist/enrichment-intents.js +2 -1
  37. package/node_modules/@oxygen/shared/dist/external-effect-arming.d.ts +54 -0
  38. package/node_modules/@oxygen/shared/dist/external-effect-arming.js +90 -0
  39. package/node_modules/@oxygen/shared/dist/geo.d.ts +247 -0
  40. package/node_modules/@oxygen/shared/dist/geo.js +314 -0
  41. package/node_modules/@oxygen/shared/dist/knowledge-seed-content.js +2 -2
  42. package/node_modules/@oxygen/shared/dist/linkedin-countries.d.ts +9 -0
  43. package/node_modules/@oxygen/shared/dist/linkedin-countries.js +1 -1
  44. package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.d.ts +48 -0
  45. package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.js +94 -0
  46. package/node_modules/@oxygen/shared/dist/managed-email-branding.d.ts +15 -0
  47. package/node_modules/@oxygen/shared/dist/managed-email-branding.js +81 -0
  48. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +20 -24
  49. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +15 -16
  50. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +3 -3
  51. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +3 -3
  52. package/node_modules/@oxygen/shared/dist/product-doctrine.js +6 -5
  53. package/node_modules/@oxygen/shared/dist/provider-balance-signal.js +3 -2
  54. package/node_modules/@oxygen/shared/dist/recipes.d.ts +1 -1
  55. package/node_modules/@oxygen/shared/dist/recipes.js +1 -0
  56. package/node_modules/@oxygen/shared/dist/repricing.d.ts +4 -14
  57. package/node_modules/@oxygen/shared/dist/repricing.js +25 -64
  58. package/node_modules/@oxygen/shared/dist/research-depth.d.ts +11 -0
  59. package/node_modules/@oxygen/shared/dist/research-depth.js +7 -0
  60. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  61. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  62. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  63. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  64. package/node_modules/@oxygen/shared/dist/whatsapp-sender-limits.d.ts +88 -0
  65. package/node_modules/@oxygen/shared/dist/whatsapp-sender-limits.js +118 -0
  66. package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.d.ts +15 -0
  67. package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.js +33 -0
  68. package/node_modules/@oxygen/shared/package.json +15 -5
  69. package/node_modules/@oxygen/workflows/dist/usage-estimate.js +9 -5
  70. package/package.json +1 -1
  71. package/node_modules/@oxygen/shared/dist/vercel-sandbox-fetch.d.ts +0 -9
  72. 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,
@@ -129,7 +129,7 @@ const SECRET_PATTERNS = [
129
129
  [/\bgh[pousr]_[A-Za-z0-9]{20,}/g, "ghx_[REDACTED]"],
130
130
  [/\bxox[baprs]-[A-Za-z0-9-]{10,}/g, "xox-[REDACTED]"],
131
131
  [/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{6,}/g, "[REDACTED_JWT]"],
132
- [/\/skills-access\/v1\.[A-Za-z0-9_-]+\.\d+\.[A-Za-z0-9_-]+/g, "/skills-access/[REDACTED_GRANT]"],
132
+ [/\/skills-access\/v[12]\.[A-Za-z0-9_-]+\.\d+\.(?:[A-Za-z0-9_-]+\.)?[A-Za-z0-9_-]+/g, "/skills-access/[REDACTED_GRANT]"],
133
133
  [/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]{16,}/gi, "$1 [REDACTED]"],
134
134
  // "secret-ish key": "value" / secret_ish_key=value (JSON + env shapes)
135
135
  [/("?(?:api[_-]?key|apikey|secret|password|passwd|token|access[_-]?token|refresh[_-]?token|client[_-]?secret|authorization|auth[_-]?token|private[_-]?key)"?\s*[:=]\s*"?)([^"\s,}]{6,})/gi, "$1[REDACTED]"],
@@ -403,6 +403,22 @@ function tokenize(expression, options = {}) {
403
403
  index += 1;
404
404
  continue;
405
405
  }
406
+ // A `{{column}}` template token is the most common paste from another tool
407
+ // (and from an AI or tool column's prompt here). It stays the same typed
408
+ // syntax error at the same position; the hint names the bare-identifier form
409
+ // and the expression with every token unwrapped. Nothing is accepted:
410
+ // `{{` is still not formula syntax (OXP-1.27.6 AC4).
411
+ if (char === "{" && expression[index + 1] === "{") {
412
+ const suggested = expression.replace(/\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g, "$1");
413
+ const example = suggested !== expression ? suggested : "upper(company_name)";
414
+ throw formulaExpressionError(`Unexpected character in formula expression. Formulas name columns as bare identifiers, not {{templates}}: write ${example}.`, {
415
+ position: index,
416
+ character: char,
417
+ expression,
418
+ hint: "Formulas read columns by bare key (company_name), not {{company_name}}; {{ }} is template syntax for AI and tool column prompts.",
419
+ ...(suggested !== expression ? { suggested_expression: suggested } : {}),
420
+ });
421
+ }
406
422
  throw formulaExpressionError("Unexpected character in formula expression.", {
407
423
  position: index,
408
424
  character: char,
@@ -0,0 +1,30 @@
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;
23
+ /**
24
+ * One provider attempt with its lane named the same way for every waterfall.
25
+ * Person waterfalls name a lane by `operation`; the company waterfall names it
26
+ * by `toolId`. Readers that key on `operation` dropped every company attempt,
27
+ * and with it the identity evidence the attempt carried (OXP-7.3.40), so each
28
+ * cell renderer normalizes through here before validating an attempt.
29
+ */
30
+ export declare function normalizeAttemptLane(value: unknown): unknown;
@@ -0,0 +1,98 @@
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
+ }
84
+ /**
85
+ * One provider attempt with its lane named the same way for every waterfall.
86
+ * Person waterfalls name a lane by `operation`; the company waterfall names it
87
+ * by `toolId`. Readers that key on `operation` dropped every company attempt,
88
+ * and with it the identity evidence the attempt carried (OXP-7.3.40), so each
89
+ * cell renderer normalizes through here before validating an attempt.
90
+ */
91
+ export function normalizeAttemptLane(value) {
92
+ if (!value || typeof value !== "object" || Array.isArray(value))
93
+ return value;
94
+ const record = value;
95
+ if (typeof record.operation === "string" || typeof record.toolId !== "string")
96
+ return value;
97
+ return { ...record, operation: record.toolId };
98
+ }
@@ -91,6 +91,8 @@ export const NATIVE_API_KEY_CONNECT_INTEGRATION_IDS = new Set([
91
91
  "serper",
92
92
  "signalbase",
93
93
  "up2data",
94
+ // whitewhale: BYOK-only; its connect form takes the API key and a user email.
95
+ "whitewhale",
94
96
  "zapmail",
95
97
  "zerobounce",
96
98
  ]);
@@ -90,10 +90,11 @@ export declare function inferPrimitiveRoute(query: string): PrimitiveRouteMatch
90
90
  */
91
91
  export declare const RETIRED_PRODUCT_SKILLS: readonly string[];
92
92
  /** Current hosted instruction audiences, shared by discovery and Agent snapshots. */
93
- export declare const OXYGEN_PRODUCT_SKILL_AUDIENCES: Readonly<Record<string, "workspace" | "agency_partner">>;
93
+ export declare const OXYGEN_PRODUCT_SKILL_AUDIENCES: Readonly<Record<string, "workspace" | "agency_partner" | "internal_dev">>;
94
94
  export declare function canReadProductSkillInstructions(name: string, entitlements: {
95
95
  authenticated?: boolean;
96
96
  agencyPartner: boolean;
97
+ internalDev?: boolean;
97
98
  }): boolean;
98
99
  /** Identify hosted instruction bodies by native tool lineage, never by user prose. */
99
100
  export declare function readProductSkillInstructionResult(capabilityName: string, result: unknown): {
@@ -119,6 +120,12 @@ export declare const SEQUENCE_UNSUBSCRIBE_COMMAND = "oxygen sequences update <se
119
120
  * Factories, so a caller can edit the object it gets.
120
121
  */
121
122
  export declare const CAPABILITY_ROUTE_MCP_CALLS: {
123
+ connectionPrices: () => {
124
+ tool: string;
125
+ arguments: {
126
+ view: string;
127
+ };
128
+ };
122
129
  connectedAppsList: () => {
123
130
  tool: string;
124
131
  arguments: {};