@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 +1 @@
1
- export declare const OXYGEN_BUILD_VERSION = "1.1011.974";
1
+ export declare const OXYGEN_BUILD_VERSION = "1.1012.2";
@@ -1,2 +1,2 @@
1
1
  // GENERATED by scripts/ci/version-stamp.mjs from VERSION + git history. Do not edit; do not commit.
2
- export const OXYGEN_BUILD_VERSION = "1.1011.974";
2
+ export const OXYGEN_BUILD_VERSION = "1.1012.2";
@@ -49,4 +49,4 @@ export const OXYGEN_VERSION = OXYGEN_BUILD_VERSION;
49
49
  // Since 2026-09-28 the release-preparation step advances this literal to the
50
50
  // highest CLI version published on npm for at least 30 days. It never lowers
51
51
  // the floor. The source change lands on dev before the next production release.
52
- export const OXYGEN_MINIMUM_CLI_VERSION = "1.917.5";
52
+ export const OXYGEN_MINIMUM_CLI_VERSION = "1.922.14";
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The WhatsApp sender-limit overlay: the keys a WhatsApp account accepts and
3
+ * their defaults and hard maximums. One owner for the quota enforcer
4
+ * (packages/integrations/src/whatsapp-quota.ts), the limits route, the MCP and
5
+ * CLI surfaces and the web form (OXP-11.3.19): the MCP tool once advertised a
6
+ * chats_reads_per_day key the route silently dropped and maxima (400 reads,
7
+ * 7200 s spacing, 3600 s jitter) the quota never allowed.
8
+ *
9
+ * Everything stays deliberately small: WhatsApp Web bans on volume.
10
+ */
11
+ export declare const WHATSAPP_SENDER_LIMIT_KEYS: readonly ["messages_per_day", "likes_per_day", "messages_reads_per_day", "relations_reads_per_day", "total_actions_per_day", "total_reads_per_day", "min_action_spacing_seconds", "action_spacing_jitter_seconds"];
12
+ export type WhatsAppSenderLimitKey = (typeof WHATSAPP_SENDER_LIMIT_KEYS)[number];
13
+ export declare const WHATSAPP_SENDER_LIMIT_DEFAULTS: {
14
+ readonly messages_per_day: 20;
15
+ readonly likes_per_day: 20;
16
+ readonly messages_reads_per_day: 200;
17
+ readonly relations_reads_per_day: 40;
18
+ readonly total_actions_per_day: 40;
19
+ readonly total_reads_per_day: 240;
20
+ readonly min_action_spacing_seconds: 600;
21
+ readonly action_spacing_jitter_seconds: 300;
22
+ };
23
+ export declare const WHATSAPP_SENDER_LIMIT_MAXIMUMS: {
24
+ readonly messages_per_day: 40;
25
+ readonly likes_per_day: 40;
26
+ readonly messages_reads_per_day: 600;
27
+ readonly relations_reads_per_day: 100;
28
+ readonly total_actions_per_day: 80;
29
+ readonly total_reads_per_day: 700;
30
+ readonly min_action_spacing_seconds: 3600;
31
+ readonly action_spacing_jitter_seconds: 1800;
32
+ };
33
+ /** OXYGEN policy, not a provider-published guarantee of account safety. */
34
+ export declare const WHATSAPP_WARMUP_RAMP: ReadonlyArray<{
35
+ throughDay: number;
36
+ cap: number;
37
+ }>;
38
+ export declare const WHATSAPP_WARMUP_FULL_CAP: 40;
39
+ export declare const WHATSAPP_CONNECTION_COOLDOWN_MS: number;
40
+ export declare const WHATSAPP_WARMUP_DAY_BASIS: "elapsed_24_hour_periods";
41
+ export declare function whatsAppRampCapForDay(day: number): number;
42
+ export declare function whatsAppWarmupDay(anchor: Date | null, now?: Date): number;
43
+ /** Poll timestamps never advance this clock; legacy mirrors retain their creation time. */
44
+ export declare function whatsAppConnectionCooldown(sender: {
45
+ metadata?: Record<string, unknown> | null;
46
+ createdAt?: Date | string | null;
47
+ }, now?: Date): {
48
+ session_connected_at: string | null;
49
+ outreach_resume_at: string | null;
50
+ blocked: boolean;
51
+ };
52
+ /** Stored reads expose the same elapsed-day policy that dispatch enforces. */
53
+ export declare function whatsAppWarmupStatus(sender: {
54
+ warmupStartedAt: Date | null;
55
+ limitOverrides?: Partial<Record<WhatsAppSenderLimitKey, unknown>> | null;
56
+ metadata?: Record<string, unknown> | null;
57
+ createdAt?: Date | string | null;
58
+ }, now?: Date): {
59
+ session_connected_at: string | null;
60
+ outreach_resume_at: string | null;
61
+ blocked: boolean;
62
+ warmup_started_at: string | null;
63
+ warmup_day: number;
64
+ day_basis: "elapsed_24_hour_periods";
65
+ ramp_cap_today: number;
66
+ configured_daily_cap: number;
67
+ effective_daily_cap: number;
68
+ ramp: ({
69
+ from_day: number;
70
+ through_day: number;
71
+ messages_per_day: number;
72
+ } | {
73
+ from_day: number;
74
+ through_day: null;
75
+ messages_per_day: number;
76
+ })[];
77
+ connection_cooldown_hours: number;
78
+ };
79
+ /** What each WhatsApp limit counts, for MCP and CLI help. */
80
+ export declare const WHATSAPP_SENDER_LIMIT_NOTES: Record<WhatsAppSenderLimitKey, string>;
81
+ /**
82
+ * The WhatsApp limits an account runs at: each stored override, floored and
83
+ * capped at its WhatsApp maximum, else the WhatsApp default. Read from the
84
+ * overrides alone: a sender's resolved `limits` start from the LinkedIn
85
+ * defaults (300 s spacing, 150 actions, 1000 reads a day), which a WhatsApp
86
+ * number must never inherit (OXP-11.3.21).
87
+ */
88
+ export declare function resolveWhatsAppSenderLimits(overrides: Partial<Record<WhatsAppSenderLimitKey, unknown>> | null | undefined): Record<WhatsAppSenderLimitKey, number>;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The WhatsApp sender-limit overlay: the keys a WhatsApp account accepts and
3
+ * their defaults and hard maximums. One owner for the quota enforcer
4
+ * (packages/integrations/src/whatsapp-quota.ts), the limits route, the MCP and
5
+ * CLI surfaces and the web form (OXP-11.3.19): the MCP tool once advertised a
6
+ * chats_reads_per_day key the route silently dropped and maxima (400 reads,
7
+ * 7200 s spacing, 3600 s jitter) the quota never allowed.
8
+ *
9
+ * Everything stays deliberately small: WhatsApp Web bans on volume.
10
+ */
11
+ export const WHATSAPP_SENDER_LIMIT_KEYS = [
12
+ "messages_per_day",
13
+ "likes_per_day",
14
+ "messages_reads_per_day",
15
+ "relations_reads_per_day",
16
+ "total_actions_per_day",
17
+ "total_reads_per_day",
18
+ "min_action_spacing_seconds",
19
+ "action_spacing_jitter_seconds",
20
+ ];
21
+ export const WHATSAPP_SENDER_LIMIT_DEFAULTS = {
22
+ messages_per_day: 20,
23
+ likes_per_day: 20,
24
+ messages_reads_per_day: 200,
25
+ relations_reads_per_day: 40,
26
+ total_actions_per_day: 40,
27
+ total_reads_per_day: 240,
28
+ min_action_spacing_seconds: 600,
29
+ action_spacing_jitter_seconds: 300,
30
+ };
31
+ export const WHATSAPP_SENDER_LIMIT_MAXIMUMS = {
32
+ messages_per_day: 40,
33
+ likes_per_day: 40,
34
+ messages_reads_per_day: 600,
35
+ relations_reads_per_day: 100,
36
+ total_actions_per_day: 80,
37
+ total_reads_per_day: 700,
38
+ min_action_spacing_seconds: 3600,
39
+ action_spacing_jitter_seconds: 1800,
40
+ };
41
+ /** OXYGEN policy, not a provider-published guarantee of account safety. */
42
+ export const WHATSAPP_WARMUP_RAMP = [
43
+ { throughDay: 2, cap: 5 },
44
+ { throughDay: 6, cap: 10 },
45
+ { throughDay: 13, cap: 15 },
46
+ ];
47
+ export const WHATSAPP_WARMUP_FULL_CAP = WHATSAPP_SENDER_LIMIT_MAXIMUMS.messages_per_day;
48
+ export const WHATSAPP_CONNECTION_COOLDOWN_MS = 24 * 60 * 60 * 1000;
49
+ export const WHATSAPP_WARMUP_DAY_BASIS = "elapsed_24_hour_periods";
50
+ export function whatsAppRampCapForDay(day) {
51
+ return WHATSAPP_WARMUP_RAMP.find((step) => day <= step.throughDay)?.cap ?? WHATSAPP_WARMUP_FULL_CAP;
52
+ }
53
+ export function whatsAppWarmupDay(anchor, now = new Date()) {
54
+ if (!anchor || !Number.isFinite(anchor.getTime()))
55
+ return 0;
56
+ return Math.max(0, Math.floor((now.getTime() - anchor.getTime()) / WHATSAPP_CONNECTION_COOLDOWN_MS));
57
+ }
58
+ /** Poll timestamps never advance this clock; legacy mirrors retain their creation time. */
59
+ export function whatsAppConnectionCooldown(sender, now = new Date()) {
60
+ const raw = sender.metadata?.whatsapp_session_connected_at ?? sender.createdAt;
61
+ const at = raw instanceof Date ? raw : typeof raw === "string" ? new Date(raw) : null;
62
+ if (!at || !Number.isFinite(at.getTime())) {
63
+ return { session_connected_at: null, outreach_resume_at: null, blocked: true };
64
+ }
65
+ const resume = new Date(at.getTime() + WHATSAPP_CONNECTION_COOLDOWN_MS);
66
+ return { session_connected_at: at.toISOString(), outreach_resume_at: resume.toISOString(), blocked: now < resume };
67
+ }
68
+ /** Stored reads expose the same elapsed-day policy that dispatch enforces. */
69
+ export function whatsAppWarmupStatus(sender, now = new Date()) {
70
+ const day = whatsAppWarmupDay(sender.warmupStartedAt, now);
71
+ const configured = resolveWhatsAppSenderLimits(sender.limitOverrides).messages_per_day;
72
+ const ramp = whatsAppRampCapForDay(day);
73
+ return {
74
+ warmup_started_at: sender.warmupStartedAt && Number.isFinite(sender.warmupStartedAt.getTime()) ? sender.warmupStartedAt.toISOString() : null,
75
+ warmup_day: day,
76
+ day_basis: WHATSAPP_WARMUP_DAY_BASIS,
77
+ ramp_cap_today: ramp,
78
+ configured_daily_cap: configured,
79
+ effective_daily_cap: Math.min(configured, ramp),
80
+ ramp: [
81
+ ...WHATSAPP_WARMUP_RAMP.map((step, index) => ({
82
+ from_day: index === 0 ? 0 : WHATSAPP_WARMUP_RAMP[index - 1].throughDay + 1,
83
+ through_day: step.throughDay, messages_per_day: Math.min(configured, step.cap),
84
+ })),
85
+ { from_day: 14, through_day: null, messages_per_day: configured },
86
+ ],
87
+ connection_cooldown_hours: 24,
88
+ ...whatsAppConnectionCooldown(sender, now),
89
+ };
90
+ }
91
+ /** What each WhatsApp limit counts, for MCP and CLI help. */
92
+ export const WHATSAPP_SENDER_LIMIT_NOTES = {
93
+ messages_per_day: "Daily WhatsApp messages across direct warm traffic and opted-in Sequence actions. Ramp: days 0–2: 5; 3–6: 10; 7–13: 15; 14+: configured cap (20 default, 40 maximum). New outreach waits 24 hours after connection or reconnection.",
94
+ likes_per_day: "Daily reactions.",
95
+ messages_reads_per_day: "Chat and message-history reads.",
96
+ relations_reads_per_day: "Chat-list reads.",
97
+ total_actions_per_day: "All sends: messages, reactions and edits.",
98
+ total_reads_per_day: "All reads.",
99
+ min_action_spacing_seconds: "Minimum seconds between WhatsApp actions.",
100
+ action_spacing_jitter_seconds: "Random seconds added on top of the spacing floor.",
101
+ };
102
+ /**
103
+ * The WhatsApp limits an account runs at: each stored override, floored and
104
+ * capped at its WhatsApp maximum, else the WhatsApp default. Read from the
105
+ * overrides alone: a sender's resolved `limits` start from the LinkedIn
106
+ * defaults (300 s spacing, 150 actions, 1000 reads a day), which a WhatsApp
107
+ * number must never inherit (OXP-11.3.21).
108
+ */
109
+ export function resolveWhatsAppSenderLimits(overrides) {
110
+ const limits = { ...WHATSAPP_SENDER_LIMIT_DEFAULTS };
111
+ for (const key of WHATSAPP_SENDER_LIMIT_KEYS) {
112
+ const value = overrides?.[key];
113
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
114
+ limits[key] = Math.min(Math.floor(value), WHATSAPP_SENDER_LIMIT_MAXIMUMS[key]);
115
+ }
116
+ }
117
+ return limits;
118
+ }
@@ -1,2 +1,17 @@
1
1
  export declare const WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS: readonly ["consecutive_failure_count", "consecutive_capacity_failure_count", "last_failure_code", "last_failure_at", "auto_paused_at", "auto_pause_reason"];
2
2
  export declare function clearWorkflowTriggerAutoPauseMetadata(metadata: Record<string, unknown>): Record<string, unknown>;
3
+ export type WorkflowAutomationPauseCode = "automation_actions_exceeded" | "insufficient_credits";
4
+ export type TriggerAutomationPause = {
5
+ reason: string;
6
+ reasonCode?: WorkflowAutomationPauseCode | null;
7
+ blockedAt: string | null;
8
+ resumesAt: string | null;
9
+ };
10
+ /** Decode typed refusals and the two controlled messages written before codes were stored. */
11
+ export declare function readWorkflowAutomationPauseCode(code: unknown, reason: unknown): WorkflowAutomationPauseCode | null;
12
+ /** Customer copy never echoes arbitrary persisted error text or invents a credit refill date. */
13
+ export declare function workflowAutomationPauseMessage(pause: {
14
+ reason?: unknown;
15
+ reasonCode?: unknown;
16
+ resumesAt?: unknown;
17
+ }): string;
@@ -13,3 +13,36 @@ const WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEY_SET = new Set(WORKFLOW_TRIGGER_AU
13
13
  export function clearWorkflowTriggerAutoPauseMetadata(metadata) {
14
14
  return Object.fromEntries(Object.entries(metadata).filter(([key]) => !WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEY_SET.has(key)));
15
15
  }
16
+ /** Decode typed refusals and the two controlled messages written before codes were stored. */
17
+ export function readWorkflowAutomationPauseCode(code, reason) {
18
+ // An older scheduler can replace the reason without replacing a newer
19
+ // worker's code. Only a controlled legacy reason may override that code.
20
+ const text = typeof reason === "string" ? reason.trim() : "";
21
+ if (text === "automation_actions_exceeded" || text === "This workspace has reached its monthly automation action limit.") {
22
+ return "automation_actions_exceeded";
23
+ }
24
+ if (text === "insufficient_credits" || text.startsWith("Not enough credits for this automation work (")) {
25
+ return "insufficient_credits";
26
+ }
27
+ if (code === "automation_actions_exceeded" || code === "insufficient_credits")
28
+ return code;
29
+ return null;
30
+ }
31
+ /** Customer copy never echoes arbitrary persisted error text or invents a credit refill date. */
32
+ export function workflowAutomationPauseMessage(pause) {
33
+ const code = readWorkflowAutomationPauseCode(pause.reasonCode, pause.reason);
34
+ if (code === "insufficient_credits") {
35
+ return "Not enough credits for the next scheduled run. Add credits before the next scheduled attempt.";
36
+ }
37
+ if (code === "automation_actions_exceeded") {
38
+ const resume = typeof pause.resumesAt === "string" ? new Date(pause.resumesAt) : null;
39
+ if (resume && !Number.isNaN(resume.getTime())) {
40
+ const day = new Intl.DateTimeFormat("en-US", {
41
+ month: "short", day: "numeric", year: "numeric", timeZone: "UTC",
42
+ }).format(resume);
43
+ return `The monthly automation-action allowance is exhausted. Cron runs resume on ${day} when the window resets.`;
44
+ }
45
+ return "The monthly automation-action allowance is exhausted. Check billing before the next scheduled attempt.";
46
+ }
47
+ return "Automation admission was refused. Check billing before the next scheduled attempt.";
48
+ }
@@ -6,16 +6,16 @@
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
9
+ "./external-write-policy": {
10
+ "types": "./dist/external-write-policy.d.ts",
11
+ "import": "./dist/external-write-policy.js",
12
+ "default": "./dist/external-write-policy.js"
13
+ },
9
14
  "./knowledge-vault-markdown": {
10
15
  "types": "./dist/knowledge-vault-markdown.d.ts",
11
16
  "import": "./dist/knowledge-vault-markdown.js",
12
17
  "default": "./dist/knowledge-vault-markdown.js"
13
18
  },
14
- "./vercel-sandbox-fetch": {
15
- "types": "./dist/vercel-sandbox-fetch.d.ts",
16
- "import": "./dist/vercel-sandbox-fetch.js",
17
- "default": "./dist/vercel-sandbox-fetch.js"
18
- },
19
19
  ".": {
20
20
  "types": "./dist/index.d.ts",
21
21
  "import": "./dist/index.js",
@@ -201,6 +201,11 @@
201
201
  "import": "./dist/pricing-sheet.js",
202
202
  "default": "./dist/pricing-sheet.js"
203
203
  },
204
+ "./research-depth": {
205
+ "types": "./dist/research-depth.d.ts",
206
+ "import": "./dist/research-depth.js",
207
+ "default": "./dist/research-depth.js"
208
+ },
204
209
  "./copilot-plan": {
205
210
  "types": "./dist/copilot-plan.d.ts",
206
211
  "import": "./dist/copilot-plan.js",
@@ -291,6 +296,11 @@
291
296
  "types": "./dist/provider-balance-signal.d.ts",
292
297
  "import": "./dist/provider-balance-signal.js"
293
298
  },
299
+ "./attempt-provenance": {
300
+ "types": "./dist/attempt-provenance.d.ts",
301
+ "import": "./dist/attempt-provenance.js",
302
+ "default": "./dist/attempt-provenance.js"
303
+ },
294
304
  "./copilot-skills.generated": null
295
305
  },
296
306
  "dependencies": {}
@@ -1,7 +1,8 @@
1
1
  // Static automation usage estimation shared by the web workflow page and the
2
- // worker scheduler's admission gate. The unit is the workflow STEP: every step a
3
- // live run executes bills one (repricing 2026-09, decision 6.1) and rows written
4
- // inside a run are free (decision 6.2), so a row-writing step costs one step
2
+ // worker scheduler's admission gate. The unit is the workflow STEP: every
3
+ // billable node a live graph executes bills one (repricing 2026-09, decision
4
+ // 6.1). Control nodes and rows written inside a run are free (decision 6.2),
5
+ // so a row-writing step costs one step
5
6
  // however many rows it writes. The per-run count derived from a manifest is a
6
7
  // FLOOR, never a projection: recipe code emits one step per checkpoint (loops
7
8
  // multiply) and a retried attempt bills again. Surfaces present these numbers as
@@ -167,7 +168,8 @@ function estimateWorkflowGraphActionsFloor(graph) {
167
168
  }
168
169
  // Nested caps multiply, and two large ones overflow Number's exact range;
169
170
  // clamping keeps every downstream product (runs x floor) a finite number.
170
- return Math.max(1, Math.min(floor, Number.MAX_SAFE_INTEGER));
171
+ // Free control-only graphs have no step charge, including in previews.
172
+ return Math.min(floor, Number.MAX_SAFE_INTEGER);
171
173
  }
172
174
  export function estimateWorkflowAutomationUsage(manifest, cron) {
173
175
  if (!isRecord(manifest))
@@ -320,7 +322,9 @@ export function assessCronCadenceViability(input) {
320
322
  return null;
321
323
  const actionsPerRunFloor = estimateWorkflowRunAutomationActionsFloor(input.manifest);
322
324
  const floorActionsPer30Days = scheduledRunsPer30Days * actionsPerRunFloor;
323
- const runsAllowedPer30Days = Math.floor(includedActions / actionsPerRunFloor);
325
+ const runsAllowedPer30Days = actionsPerRunFloor === 0
326
+ ? MINUTES_PER_30_DAYS
327
+ : Math.floor(includedActions / actionsPerRunFloor);
324
328
  return {
325
329
  scheduledRunsPer30Days,
326
330
  actionsPerRunFloor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxygen-agent/cli",
3
- "version": "1.1011.974",
3
+ "version": "1.1012.2",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -1,9 +0,0 @@
1
- /** The Sandbox SDK retries transport failures and 429/5xx responses even for
2
- * POSTs. Visual execution cannot replay an ambiguously accepted mutation.
3
- * The SDK recognizes AbortError as non-retryable; outcomeUnknown deliberately
4
- * distinguishes this transport fence from ordinary caller cancellation. */
5
- export declare class SandboxMutationOutcomeUnknownError extends Error {
6
- readonly outcomeUnknown = true;
7
- constructor(options?: ErrorOptions);
8
- }
9
- export declare function createSandboxNonReplayFetch(rawFetch?: typeof globalThis.fetch): typeof globalThis.fetch;
@@ -1,33 +0,0 @@
1
- /** The Sandbox SDK retries transport failures and 429/5xx responses even for
2
- * POSTs. Visual execution cannot replay an ambiguously accepted mutation.
3
- * The SDK recognizes AbortError as non-retryable; outcomeUnknown deliberately
4
- * distinguishes this transport fence from ordinary caller cancellation. */
5
- export class SandboxMutationOutcomeUnknownError extends Error {
6
- outcomeUnknown = true;
7
- constructor(options) {
8
- super("Sandbox mutation acceptance requires reconciliation.", options);
9
- this.name = "AbortError";
10
- }
11
- }
12
- export function createSandboxNonReplayFetch(rawFetch = globalThis.fetch) {
13
- return async (input, init) => {
14
- const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
15
- if (["GET", "HEAD", "OPTIONS"].includes(method))
16
- return rawFetch(input, init);
17
- try {
18
- // Redirects must not resubmit a mutation either. No request URL, body or
19
- // authentication header is copied into the error or telemetry.
20
- const response = await rawFetch(input, { ...init, redirect: "error" });
21
- if (response.status === 429 || response.status >= 500) {
22
- void response.body?.cancel().catch(() => { });
23
- throw new SandboxMutationOutcomeUnknownError();
24
- }
25
- return response;
26
- }
27
- catch (error) {
28
- if (error instanceof SandboxMutationOutcomeUnknownError)
29
- throw error;
30
- throw new SandboxMutationOutcomeUnknownError({ cause: error });
31
- }
32
- };
33
- }