@oxygen-agent/cli 1.936.1 → 1.948.1

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 (33) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +24 -2
  3. package/dist/help.js +1 -0
  4. package/dist/index.js +342 -54
  5. package/dist/ugc-commands.js +353 -12
  6. package/node_modules/@oxygen/shared/dist/byok-connect.d.ts +11 -6
  7. package/node_modules/@oxygen/shared/dist/byok-connect.js +14 -6
  8. package/node_modules/@oxygen/shared/dist/capability-discovery.js +53 -5
  9. package/node_modules/@oxygen/shared/dist/email-dsn.d.ts +60 -0
  10. package/node_modules/@oxygen/shared/dist/email-dsn.js +120 -0
  11. package/node_modules/@oxygen/shared/dist/email-warmup-readiness.d.ts +64 -0
  12. package/node_modules/@oxygen/shared/dist/email-warmup-readiness.js +90 -0
  13. package/node_modules/@oxygen/shared/dist/index.d.ts +6 -0
  14. package/node_modules/@oxygen/shared/dist/index.js +6 -0
  15. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +50 -21
  16. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +47 -21
  17. package/node_modules/@oxygen/shared/dist/langfuse.d.ts +8 -3
  18. package/node_modules/@oxygen/shared/dist/langfuse.js +177 -130
  19. package/node_modules/@oxygen/shared/dist/llm-payload.d.ts +10 -0
  20. package/node_modules/@oxygen/shared/dist/llm-payload.js +54 -0
  21. package/node_modules/@oxygen/shared/dist/llm-usage.d.ts +11 -0
  22. package/node_modules/@oxygen/shared/dist/llm-usage.js +30 -0
  23. package/node_modules/@oxygen/shared/dist/product-analytics-core.d.ts +98 -0
  24. package/node_modules/@oxygen/shared/dist/product-analytics-core.js +159 -0
  25. package/node_modules/@oxygen/shared/dist/product-analytics-environment.d.ts +18 -0
  26. package/node_modules/@oxygen/shared/dist/product-analytics-environment.js +46 -0
  27. package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +92 -0
  28. package/node_modules/@oxygen/shared/dist/product-analytics-events.js +96 -0
  29. package/node_modules/@oxygen/shared/dist/ugc.d.ts +21 -1
  30. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  31. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  32. package/node_modules/@oxygen/workflows/dist/graph/lint.js +22 -0
  33. package/package.json +1 -1
@@ -28,20 +28,36 @@ export function registerUgcCommands(program, handle) {
28
28
  const ugc = program
29
29
  .command("ugc")
30
30
  .description("Manage UGC programs and independent creator participations: briefs, voice, posts, approvals, sponsorship and bounded amplification. Requires UGC availability.");
31
+ const groupDescriptions = {
32
+ programs: "Programs you operate or joined. Lifecycle: create, update, duplicate (settings + briefs), archive (stops future work, keeps shared history, reversible with restore) and delete (permanent; only while no creator has ever joined).",
33
+ creators: "Creator participations: invite, revoke an invite, accept in the creator workspace, connect a sender, change approval authority, or revoke.",
34
+ briefs: "Program briefs with ordered questions for creators.",
35
+ posts: "Enrolled program posts: enroll existing posts, draft from briefs, edit, schedule, and record creator or brand approval.",
36
+ analytics: "Stored daily program and creator performance.",
37
+ knowledge: "Master Knowledge pages shared live with a program.",
38
+ voice: "Creator voice profiles and imports.",
39
+ sponsorship: "Seat and credit sponsorship per participation.",
40
+ amplification: "Bounded, approval-gated amplification of enrolled posts.",
41
+ calendar: "Bounded enrolled-post calendar reads.",
42
+ };
31
43
  const groups = new Map();
32
44
  const group = (name) => {
33
45
  let command = groups.get(name);
34
46
  if (!command) {
35
47
  command = ugc.command(name);
48
+ const description = groupDescriptions[name];
49
+ if (description)
50
+ command.description(description);
36
51
  groups.set(name, command);
37
52
  }
38
53
  return command;
39
54
  };
40
55
  group("programs")
41
56
  .command("list")
42
- .description("Read UGC programs in the active workspace. Read-only.")
57
+ .description("Read UGC programs in the active workspace, archived ones included unless --status filters. Read-only.")
43
58
  .option("--program-id <id>", "Program ID.")
44
59
  .option("--participation-id <id>", "Creator participation ID.")
60
+ .option("--status <active|archived>", "Only programs in this status.")
45
61
  .option("--json", "Print a JSON envelope.")
46
62
  .action((options) => handle("ugc programs list", options, () => {
47
63
  const query = new URLSearchParams({ view: "programs" });
@@ -49,6 +65,8 @@ export function registerUgcCommands(program, handle) {
49
65
  query.set("program_id", String(options.programId));
50
66
  if (options.participationId)
51
67
  query.set("participation_id", String(options.participationId));
68
+ if (options.status)
69
+ query.set("status", String(options.status));
52
70
  return requestOxygen(`/api/cli/ugc?${query}`);
53
71
  }));
54
72
  group("programs")
@@ -67,7 +85,7 @@ export function registerUgcCommands(program, handle) {
67
85
  }));
68
86
  group("creators")
69
87
  .command("list")
70
- .description("Read UGC participations in the active workspace. Read-only.")
88
+ .description("Read creators and invitations, including persisted email delivery status. Read-only.")
71
89
  .option("--program-id <id>", "Program ID.")
72
90
  .option("--participation-id <id>", "Creator participation ID.")
73
91
  .option("--json", "Print a JSON envelope.")
@@ -119,6 +137,8 @@ export function registerUgcCommands(program, handle) {
119
137
  .description("Read UGC analytics in the active workspace. Read-only.")
120
138
  .requiredOption("--program-id <id>", "Program ID.")
121
139
  .option("--participation-id <id>", "Creator participation ID.")
140
+ .option("--post-cursor <cursor>", "Next post_performance page from next_post_cursor; totals still cover all enrolled posts.")
141
+ .option("--limit <count>", "Post performance rows per page, up to 100.")
122
142
  .option("--json", "Print a JSON envelope.")
123
143
  .action((options) => handle("ugc analytics get", options, () => {
124
144
  const query = new URLSearchParams({ view: "analytics" });
@@ -126,6 +146,10 @@ export function registerUgcCommands(program, handle) {
126
146
  query.set("program_id", String(options.programId));
127
147
  if (options.participationId)
128
148
  query.set("participation_id", String(options.participationId));
149
+ if (options.postCursor)
150
+ query.set("post_cursor", String(options.postCursor));
151
+ if (options.limit)
152
+ query.set("limit", String(options.limit));
129
153
  return requestOxygen(`/api/cli/ugc?${query}`);
130
154
  }));
131
155
  group("knowledge")
@@ -156,9 +180,23 @@ export function registerUgcCommands(program, handle) {
156
180
  query.set("participation_id", String(options.participationId));
157
181
  return requestOxygen(`/api/cli/ugc?${query}`);
158
182
  }));
183
+ group("posts")
184
+ .command("draft-status")
185
+ .description("Read the original paid draft request and saved result without generating again. Creator workspace only, 0 credits.")
186
+ .requiredOption("--participation-id <id>", "Creator participation ID.")
187
+ .requiredOption("--request-id <id>", "Original draft request ID.")
188
+ .option("--json", "Print a JSON envelope.")
189
+ .action((options) => handle("ugc posts draft-status", options, () => {
190
+ const query = new URLSearchParams({
191
+ view: "draft_run",
192
+ participation_id: String(options.participationId),
193
+ request_id: String(options.requestId),
194
+ });
195
+ return requestOxygen(`/api/cli/ugc?${query}`);
196
+ }));
159
197
  group("sponsorship")
160
198
  .command("get")
161
- .description("Read UGC sponsorship in the active workspace. Read-only.")
199
+ .description("Read sponsorship in the active workspace. Creators see their remaining credits and exact organizer access; masters see program payment status. Read-only.")
162
200
  .requiredOption("--program-id <id>", "Program ID.")
163
201
  .option("--participation-id <id>", "Creator participation ID.")
164
202
  .option("--json", "Print a JSON envelope.")
@@ -170,6 +208,66 @@ export function registerUgcCommands(program, handle) {
170
208
  query.set("participation_id", String(options.participationId));
171
209
  return requestOxygen(`/api/cli/ugc?${query}`);
172
210
  }));
211
+ for (const spec of [
212
+ {
213
+ group: "calendar",
214
+ command: "list",
215
+ view: "calendar",
216
+ description: "Read the program's enrolled post calendar in a bounded UTC date range (up to 42 days). Stored data only, 0 credits.",
217
+ fields: [
218
+ ["date_from", "Start ISO instant, inclusive.", true],
219
+ ["date_to", "End ISO instant, exclusive.", true],
220
+ [
221
+ "calendar_cursor",
222
+ "Calendar next_cursor from the previous page.",
223
+ false,
224
+ ],
225
+ ["participation_id", "Filter to one creator participation.", false],
226
+ ],
227
+ },
228
+ {
229
+ group: "posts",
230
+ command: "history",
231
+ view: "post_history",
232
+ description: "Read retained public comments, metrics, and publication receipts for one enrolled post. Stored data only, 0 credits.",
233
+ fields: [
234
+ ["post_id", "Enrolled source post UUID.", true],
235
+ ["cursor", "next_comments_cursor from the previous page.", false],
236
+ ["metric_cursor", "next_metrics_cursor from the previous page.", false],
237
+ ["action_cursor", "next_actions_cursor from the previous page.", false],
238
+ ],
239
+ },
240
+ {
241
+ group: "amplification",
242
+ command: "get",
243
+ view: "amplification",
244
+ description: "Read this program's current amplification authority and immutable action history. Master workspace only; 0 credits, no provider call.",
245
+ fields: [["cursor", "next_cursor from the previous action page.", false]],
246
+ },
247
+ ]) {
248
+ const command = group(spec.group)
249
+ .command(spec.command)
250
+ .description(spec.description)
251
+ .requiredOption("--program-id <id>", "Program ID.")
252
+ .option("--limit <count>", "Rows per page, up to 100.")
253
+ .option("--json", "Print a JSON envelope.");
254
+ for (const [name, description, required] of spec.fields)
255
+ command[required ? "requiredOption" : "option"](`--${name.replaceAll("_", "-")} <value>`, description);
256
+ command.action((options) => handle(`ugc ${spec.group} ${spec.command}`, options, () => {
257
+ const query = new URLSearchParams({
258
+ view: spec.view,
259
+ program_id: String(options.programId),
260
+ });
261
+ for (const [name] of spec.fields) {
262
+ const key = name.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
263
+ if (options[key] !== undefined)
264
+ query.set(name, String(options[key]));
265
+ }
266
+ if (options.limit)
267
+ query.set("limit", String(options.limit));
268
+ return requestOxygen(`/api/cli/ugc?${query}`);
269
+ }));
270
+ }
173
271
  const writes = [
174
272
  {
175
273
  action: "program_create",
@@ -252,7 +350,79 @@ export function registerUgcCommands(program, handle) {
252
350
  {
253
351
  name: "status",
254
352
  kind: "string",
255
- description: "Program status, or revoked for a participation.",
353
+ description: "active or archived; prefer `programs archive` / `programs restore`.",
354
+ required: false,
355
+ },
356
+ ],
357
+ },
358
+ {
359
+ action: "program_update",
360
+ command: "programs archive",
361
+ description: "Archive a program: stop future access, publishing, tracking and amplification, revoke pending invitations and request cancellation of sponsorship renewals. Shared history is retained; `programs restore` re-activates it.",
362
+ fixed: { status: "archived" },
363
+ fields: [
364
+ {
365
+ name: "program_id",
366
+ kind: "string",
367
+ description: "Program ID.",
368
+ required: true,
369
+ },
370
+ ],
371
+ },
372
+ {
373
+ action: "program_update",
374
+ command: "programs restore",
375
+ description: "Restore an archived program. Creators removed earlier stay removed, revoked invitations stay revoked, and cancelled sponsorships need a new preview and confirmation.",
376
+ fixed: { status: "active" },
377
+ fields: [
378
+ {
379
+ name: "program_id",
380
+ kind: "string",
381
+ description: "Program ID.",
382
+ required: true,
383
+ },
384
+ ],
385
+ },
386
+ {
387
+ action: "program_duplicate",
388
+ command: "programs duplicate",
389
+ description: "Copy a program's settings, shared knowledge and brand review into a new active program, with its briefs by default. Free: 0 credits. Never copies creators, invitations, posts or sponsorship.",
390
+ fields: [
391
+ {
392
+ name: "program_id",
393
+ kind: "string",
394
+ description: "Source program ID.",
395
+ required: true,
396
+ },
397
+ {
398
+ name: "name",
399
+ kind: "string",
400
+ description: 'Name of the copy; defaults to "<name> (copy)".',
401
+ required: false,
402
+ },
403
+ {
404
+ name: "include_briefs",
405
+ kind: "boolean",
406
+ description: "Copy the program's briefs (default true).",
407
+ required: false,
408
+ },
409
+ ],
410
+ },
411
+ {
412
+ action: "program_delete",
413
+ command: "programs delete",
414
+ description: "Permanently delete a program with its briefs and pending invitations. A bare call previews the counts and whether it is deletable; pass --approved to delete. Allowed only while no creator has ever joined; otherwise use `programs archive`.",
415
+ fields: [
416
+ {
417
+ name: "program_id",
418
+ kind: "string",
419
+ description: "Program ID.",
420
+ required: true,
421
+ },
422
+ {
423
+ name: "approved",
424
+ kind: "boolean",
425
+ description: "Confirm the previewed permanent deletion.",
256
426
  required: false,
257
427
  },
258
428
  ],
@@ -260,7 +430,7 @@ export function registerUgcCommands(program, handle) {
260
430
  {
261
431
  action: "invite_create",
262
432
  command: "creators invite",
263
- description: "Create a shareable invite for an independent creator. No email is sent.",
433
+ description: "Preview an invitation email (0 credits); send with --approved --preview-token from that preview. Use --link-only to create a link without email.",
264
434
  fields: [
265
435
  {
266
436
  name: "program_id",
@@ -271,9 +441,52 @@ export function registerUgcCommands(program, handle) {
271
441
  {
272
442
  name: "email",
273
443
  kind: "string",
274
- description: "Invitee email. Returns a shareable invitation; does not send email.",
444
+ description: "Exact creator email to receive the invitation.",
445
+ required: true,
446
+ },
447
+ {
448
+ name: "approved",
449
+ kind: "boolean",
450
+ description: "Authorize sending the exact previewed invitation email.",
451
+ required: false,
452
+ },
453
+ {
454
+ name: "preview_token",
455
+ kind: "string",
456
+ description: "Exact token from the invitation email preview.",
457
+ required: false,
458
+ },
459
+ ],
460
+ },
461
+ {
462
+ action: "invite_send",
463
+ command: "creators send-invite",
464
+ description: "Preview email for an existing pending invitation; send or resend with --approved --preview-token. Replaces the old link, keeps its expiry. Sent means provider accepted, not inbox delivery.",
465
+ fields: [
466
+ {
467
+ name: "program_id",
468
+ kind: "string",
469
+ description: "Program ID.",
470
+ required: true,
471
+ },
472
+ {
473
+ name: "invitation_id",
474
+ kind: "string",
475
+ description: "Exact pending invitation ID from creators list.",
275
476
  required: true,
276
477
  },
478
+ {
479
+ name: "approved",
480
+ kind: "boolean",
481
+ description: "Authorize the exact previewed email send or resend.",
482
+ required: false,
483
+ },
484
+ {
485
+ name: "preview_token",
486
+ kind: "string",
487
+ description: "Exact token from this invitation email preview.",
488
+ required: false,
489
+ },
277
490
  ],
278
491
  },
279
492
  {
@@ -453,8 +666,14 @@ export function registerUgcCommands(program, handle) {
453
666
  {
454
667
  action: "draft_create",
455
668
  command: "posts draft",
456
- description: "Preview AI drafting from brief answers; execute only with approved and a credit ceiling.",
669
+ description: "Preview AI drafting from brief answers. Execute only in the creator workspace with approved and a credit ceiling; publishing delegation does not grant authority to spend creator credits.",
457
670
  fields: [
671
+ {
672
+ name: "request_id",
673
+ kind: "string",
674
+ description: "Request ID from preview. Reuse it after a timeout; a new ID is a new paid draft.",
675
+ required: false,
676
+ },
458
677
  {
459
678
  name: "participation_id",
460
679
  kind: "string",
@@ -589,7 +808,7 @@ export function registerUgcCommands(program, handle) {
589
808
  {
590
809
  action: "voice_import",
591
810
  command: "voice import",
592
- description: "Preview LinkedIn voice import; execute only with approved and a credit ceiling.",
811
+ description: "Preview all retrievable LinkedIn history and voice inference; approve a total credit ceiling to start. Use --import-id to resume a safely interrupted import with its saved progress and spend.",
593
812
  fields: [
594
813
  {
595
814
  name: "participation_id",
@@ -597,6 +816,12 @@ export function registerUgcCommands(program, handle) {
597
816
  description: "Creator participation ID.",
598
817
  required: true,
599
818
  },
819
+ {
820
+ name: "import_id",
821
+ kind: "string",
822
+ description: "Existing safely interrupted import to resume; max-credits is its total ceiling including credits already used.",
823
+ required: false,
824
+ },
600
825
  {
601
826
  name: "approved",
602
827
  kind: "boolean",
@@ -611,10 +836,66 @@ export function registerUgcCommands(program, handle) {
611
836
  },
612
837
  ],
613
838
  },
839
+ {
840
+ action: "voice_save",
841
+ command: "voice save",
842
+ description: "Save your own writing preferences and examples as a private provisional Knowledge voice. Free: 0 credits, no provider call or connected account required. Existing voice edits require its current revision.",
843
+ fields: [
844
+ {
845
+ name: "participation_id",
846
+ kind: "string",
847
+ description: "Your creator participation ID.",
848
+ required: true,
849
+ },
850
+ {
851
+ name: "body",
852
+ kind: "string",
853
+ description: "Your own writing preferences or voice guide (Markdown).",
854
+ required: true,
855
+ },
856
+ {
857
+ name: "writing_examples",
858
+ kind: "array",
859
+ description: "JSON array of up to five examples you actually wrote; do not invent personal experiences.",
860
+ required: false,
861
+ },
862
+ {
863
+ name: "expected_revision",
864
+ kind: "number",
865
+ description: "Current profile revision returned by voice get, required when editing it.",
866
+ required: false,
867
+ },
868
+ ],
869
+ },
870
+ {
871
+ action: "sponsorship_cancel",
872
+ command: "sponsorship cancel",
873
+ description: "Preview stopping renewal for one program sponsorship. Confirm with --approved and a signed-in master admin. Participation and credits through the paid period remain available.",
874
+ fields: [
875
+ {
876
+ name: "program_id",
877
+ kind: "string",
878
+ description: "Master program ID.",
879
+ required: true,
880
+ },
881
+ {
882
+ name: "participation_id",
883
+ kind: "string",
884
+ description: "Exact creator participation ID.",
885
+ required: true,
886
+ },
887
+ {
888
+ name: "approved",
889
+ kind: "boolean",
890
+ description: "Approve the exact cancellation preview. Omit for a free preview.",
891
+ required: false,
892
+ },
893
+ ],
894
+ },
614
895
  {
615
896
  action: "sponsorship_preview",
616
897
  command: "sponsorship preview",
617
- description: "Preview seat charges and monthly 10,000-credit creator grants for this program.",
898
+ description: "Preview seat charges and monthly 10,000-credit creator grants as a signed-in master workspace admin. Org API keys cannot create this quote.",
618
899
  fields: [
619
900
  {
620
901
  name: "program_id",
@@ -633,7 +914,7 @@ export function registerUgcCommands(program, handle) {
633
914
  {
634
915
  action: "sponsorship_confirm",
635
916
  command: "sponsorship confirm",
636
- description: "Approve the exact sponsorship quote; each master program pays separately even for the same account.",
917
+ description: "Confirm the exact sponsorship quote as the same signed-in master workspace admin. Each program pays separately, even for the same account.",
637
918
  fields: [
638
919
  {
639
920
  name: "program_id",
@@ -661,11 +942,54 @@ export function registerUgcCommands(program, handle) {
661
942
  },
662
943
  ],
663
944
  },
945
+ {
946
+ action: "amplification_reconcile",
947
+ command: "amplification reconcile",
948
+ description: "Preview recording a provider observation for an uncertain program action. Confirmation needs a signed-in operator and --approved; it never contacts the provider or authorizes a retry.",
949
+ fields: [
950
+ {
951
+ name: "program_id",
952
+ kind: "string",
953
+ description: "Program ID in the master workspace.",
954
+ required: true,
955
+ },
956
+ {
957
+ name: "action_id",
958
+ kind: "string",
959
+ description: "Unresolved effect_unknown action ID.",
960
+ required: true,
961
+ },
962
+ {
963
+ name: "outcome",
964
+ kind: "string",
965
+ description: "Observed applied or not_applied outcome.",
966
+ required: true,
967
+ },
968
+ {
969
+ name: "evidence",
970
+ kind: "object",
971
+ description: "JSON object: provider_post_id, evidence_ref, observed_at ISO instant; applied needs provider_object_id and comments/reposts need exact observed_text. Optional note.",
972
+ required: true,
973
+ },
974
+ {
975
+ name: "approved",
976
+ kind: "boolean",
977
+ description: "Explicitly confirm this immutable observation. No retry is authorized.",
978
+ required: false,
979
+ },
980
+ ],
981
+ },
664
982
  {
665
983
  action: "amplification_save",
666
984
  command: "amplification save",
667
985
  description: "Save exact opted-in sender scope and bounded post-specific amplification. Enabling needs explicit approval.",
668
986
  fields: [
987
+ {
988
+ name: "sender_voice_pages",
989
+ kind: "object",
990
+ description: "JSON map from each acting sender UUID to its selected active Knowledge voice page UUID. Required for comments or reposts; the exact mapping is approved with the policy.",
991
+ required: false,
992
+ },
669
993
  {
670
994
  name: "program_id",
671
995
  kind: "string",
@@ -733,16 +1057,33 @@ export function registerUgcCommands(program, handle) {
733
1057
  else
734
1058
  command.option(flag, field.description);
735
1059
  }
1060
+ if (spec.action === "invite_create")
1061
+ command.option("--link-only", "Create a shareable link without sending email.");
736
1062
  command
737
1063
  .option("--json", "Print a JSON envelope.")
738
1064
  .action((options) => handle(`ugc ${spec.command}`, options, () => {
739
- const body = { action: spec.action };
1065
+ const body = {
1066
+ action: spec.action,
1067
+ ...(spec.fixed ?? {}),
1068
+ };
740
1069
  for (const field of spec.fields) {
741
1070
  const key = field.name.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
742
1071
  if (options[key] !== undefined)
743
1072
  body[field.name] = parseField(options[key], field);
744
1073
  }
745
- return requestOxygen("/api/cli/ugc", { method: "POST", body });
1074
+ if (spec.action === "invite_create") {
1075
+ body.send_email = options.linkOnly !== true;
1076
+ if (options.linkOnly && (body.approved || body.preview_token))
1077
+ throw new Error("--link-only cannot be combined with --approved or --preview-token.");
1078
+ }
1079
+ return requestOxygen("/api/cli/ugc", {
1080
+ method: "POST",
1081
+ body,
1082
+ ...(spec.action === "draft_create" &&
1083
+ typeof body.request_id === "string"
1084
+ ? { idempotencyKey: body.request_id }
1085
+ : {}),
1086
+ });
746
1087
  }));
747
1088
  }
748
1089
  }
@@ -4,10 +4,10 @@
4
4
  *
5
5
  * WHY THIS EXISTS. `native_provider.execution.supports_byok` says a TOOL can run on
6
6
  * a customer's own key. It does not say the customer has any way to give us one.
7
- * Those are two different facts, and 10 of the 42 byok-capable native providers
7
+ * Those are two different facts, and 9 of the 42 byok-capable native providers
8
8
  * have the first without the second:
9
9
  *
10
- * apify builtwith dropleads lusha openwebninja parallel peopledatalabs
10
+ * apify builtwith dropleads lusha openwebninja peopledatalabs
11
11
  * rocketreach theirstack wiza
12
12
  *
13
13
  * For those, `oxygen integrations connect <id> --api-key` does not reach
@@ -17,11 +17,16 @@
17
17
  * tool is native generic-REST and `shouldRouteStaticToolThroughComposio` is false, so
18
18
  * `resolveOptionalApiKeyCredentials` finds nothing and the call falls back to the
19
19
  * MANAGED key — the very key that was out of credits. The command appears to work,
20
- * changes nothing, and the customer is back where they started with no error.
20
+ * changes nothing, and the customer is back where they started with no error. The
21
+ * connect route now refuses each of those nine with a typed `byok_connect_unavailable`
22
+ * rather than a silent Composio "success"; they stay OFF this list until one gains a
23
+ * real native connect form.
21
24
  *
22
- * That is precisely the failure Plain T-104 would have hit: `parallel.search` was one
23
- * of the refused tools, and a refusal message that told Mentcape to connect their own
24
- * Parallel key would have sent them down a path that silently does nothing.
25
+ * That is precisely the failure Plain T-104 hit: `parallel.search` was one of the
26
+ * refused tools, and a refusal message that told Mentcape to connect their own Parallel
27
+ * key would once have sent them down a path that silently does nothing. Parallel now has
28
+ * a native api-key connect (catalog.ts) and IS on this list, so that message is finally
29
+ * true for it — the nine above are the ones it must still never name.
25
30
  *
26
31
  * So a message may only offer BYOK when BOTH halves hold, and this list is the second
27
32
  * half. It lives in @oxygen/shared because the two sides that need it cannot see each
@@ -4,10 +4,10 @@
4
4
  *
5
5
  * WHY THIS EXISTS. `native_provider.execution.supports_byok` says a TOOL can run on
6
6
  * a customer's own key. It does not say the customer has any way to give us one.
7
- * Those are two different facts, and 10 of the 42 byok-capable native providers
7
+ * Those are two different facts, and 9 of the 42 byok-capable native providers
8
8
  * have the first without the second:
9
9
  *
10
- * apify builtwith dropleads lusha openwebninja parallel peopledatalabs
10
+ * apify builtwith dropleads lusha openwebninja peopledatalabs
11
11
  * rocketreach theirstack wiza
12
12
  *
13
13
  * For those, `oxygen integrations connect <id> --api-key` does not reach
@@ -17,11 +17,16 @@
17
17
  * tool is native generic-REST and `shouldRouteStaticToolThroughComposio` is false, so
18
18
  * `resolveOptionalApiKeyCredentials` finds nothing and the call falls back to the
19
19
  * MANAGED key — the very key that was out of credits. The command appears to work,
20
- * changes nothing, and the customer is back where they started with no error.
20
+ * changes nothing, and the customer is back where they started with no error. The
21
+ * connect route now refuses each of those nine with a typed `byok_connect_unavailable`
22
+ * rather than a silent Composio "success"; they stay OFF this list until one gains a
23
+ * real native connect form.
21
24
  *
22
- * That is precisely the failure Plain T-104 would have hit: `parallel.search` was one
23
- * of the refused tools, and a refusal message that told Mentcape to connect their own
24
- * Parallel key would have sent them down a path that silently does nothing.
25
+ * That is precisely the failure Plain T-104 hit: `parallel.search` was one of the
26
+ * refused tools, and a refusal message that told Mentcape to connect their own Parallel
27
+ * key would once have sent them down a path that silently does nothing. Parallel now has
28
+ * a native api-key connect (catalog.ts) and IS on this list, so that message is finally
29
+ * true for it — the nine above are the ones it must still never name.
25
30
  *
26
31
  * So a message may only offer BYOK when BOTH halves hold, and this list is the second
27
32
  * half. It lives in @oxygen/shared because the two sides that need it cannot see each
@@ -64,6 +69,9 @@ export const NATIVE_API_KEY_CONNECT_INTEGRATION_IDS = new Set([
64
69
  // rest — so the native runner does find the key.
65
70
  "moltsets",
66
71
  "ocean",
72
+ // Native api-key connect added in catalog.ts (2026-09-10): connect writes an
73
+ // integration_secrets api_key row the native parallel.* runner reads. T-104.
74
+ "parallel",
67
75
  "predictleads",
68
76
  "prospeo",
69
77
  "scalelist",