@oxygen-agent/cli 1.1014.1600 → 1.1014.1763
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.
- package/README.md +1 -1
- package/dist/command-families/program.js +4 -0
- package/dist/command-manifest.js +18 -3
- package/dist/command-support.js +1 -1
- package/dist/commands/admin.js +82 -1
- package/dist/commands/billing.js +1 -1
- package/dist/commands/copilot.js +29 -7
- package/dist/commands/custom-integrations.js +13 -0
- package/dist/commands/domains.js +7 -0
- package/dist/commands/email-assets.d.ts +3 -0
- package/dist/commands/email-assets.js +78 -0
- package/dist/commands/email-templates.d.ts +3 -0
- package/dist/commands/email-templates.js +101 -0
- package/dist/commands/inbox.js +1 -1
- package/dist/commands/integrations.js +5 -1
- package/dist/commands/mailboxes.js +26 -6
- package/dist/commands/managed-inboxes.js +2 -2
- package/dist/commands/senders.js +4 -4
- package/dist/commands/sequences.js +47 -12
- package/dist/commands/subscriptions.d.ts +2 -4
- package/dist/commands/subscriptions.js +297 -51
- package/dist/credential-storage/store.d.ts +10 -0
- package/dist/credential-storage/store.js +282 -0
- package/dist/credentials.d.ts +1 -3
- package/dist/credentials.js +3 -275
- package/dist/mailbox-fleet-output.js +11 -2
- package/dist/sequence-start-at.d.ts +7 -0
- package/dist/sequence-start-at.js +21 -0
- package/dist/sequences-brand-kit-command.d.ts +28 -0
- package/dist/sequences-brand-kit-command.js +83 -0
- package/dist/sequences-content-commands.d.ts +28 -0
- package/dist/sequences-content-commands.js +176 -0
- package/dist/sequences-newsletter-commands.d.ts +6 -0
- package/dist/sequences-newsletter-commands.js +108 -0
- package/dist/sequences-test-send-command.d.ts +3 -0
- package/dist/sequences-test-send-command.js +47 -0
- package/dist/workspace-placement-commands.js +3 -2
- package/node_modules/@oxygen/formula/dist/evaluate.js +2 -4
- package/node_modules/@oxygen/formula/dist/expression.d.ts +7 -5
- package/node_modules/@oxygen/formula/dist/expression.js +26 -4
- package/node_modules/@oxygen/formula/dist/formula-functions.d.ts +6 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.js +55 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +2 -2
- package/node_modules/@oxygen/shared/dist/billing.js +5 -7
- package/node_modules/@oxygen/shared/dist/bulk-email-pacing.d.ts +105 -0
- package/node_modules/@oxygen/shared/dist/bulk-email-pacing.js +125 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +29 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +236 -18
- package/node_modules/@oxygen/shared/dist/clay-migration-auth-state.d.ts +7 -1
- package/node_modules/@oxygen/shared/dist/clay-migration-auth-state.js +6 -5
- package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +1 -0
- package/node_modules/@oxygen/shared/dist/contracts/clay-migration.d.ts +40 -1
- package/node_modules/@oxygen/shared/dist/contracts/clay-migration.js +60 -6
- package/node_modules/@oxygen/shared/dist/contracts/copilot-attachment-read.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/contracts/copilot-attachment-read.js +7 -0
- package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +2 -1
- package/node_modules/@oxygen/shared/dist/contracts/teammates.js +6 -1
- package/node_modules/@oxygen/shared/dist/copilot-journeys.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/copilot-playbooks.generated.js +2 -2
- package/node_modules/@oxygen/shared/dist/email-brand-kit.d.ts +108 -0
- package/node_modules/@oxygen/shared/dist/email-brand-kit.js +214 -0
- package/node_modules/@oxygen/shared/dist/email-classification-scope.d.ts +35 -0
- package/node_modules/@oxygen/shared/dist/email-classification-scope.js +40 -0
- package/node_modules/@oxygen/shared/dist/email-delivery.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/email-delivery.js +14 -0
- package/node_modules/@oxygen/shared/dist/email-documents.d.ts +61 -0
- package/node_modules/@oxygen/shared/dist/email-documents.js +143 -0
- package/node_modules/@oxygen/shared/dist/email-dsn.js +52 -10
- package/node_modules/@oxygen/shared/dist/email-html-sanitize.d.ts +23 -0
- package/node_modules/@oxygen/shared/dist/email-html-sanitize.js +96 -0
- package/node_modules/@oxygen/shared/dist/email-subscriptions.d.ts +63 -1
- package/node_modules/@oxygen/shared/dist/email-subscriptions.js +149 -1
- package/node_modules/@oxygen/shared/dist/email-unsubscribe-token.d.ts +41 -2
- package/node_modules/@oxygen/shared/dist/email-unsubscribe-token.js +50 -1
- package/node_modules/@oxygen/shared/dist/native-chat.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/native-chat.js +12 -1
- package/node_modules/@oxygen/shared/dist/newsletter-analytics.d.ts +90 -0
- package/node_modules/@oxygen/shared/dist/newsletter-analytics.js +28 -0
- package/node_modules/@oxygen/shared/dist/newsletter-test-send.d.ts +20 -0
- package/node_modules/@oxygen/shared/dist/newsletter-test-send.js +46 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +12 -0
- package/node_modules/@oxygen/shared/dist/onboarding-research.d.ts +9 -2
- package/node_modules/@oxygen/shared/dist/onboarding-research.js +9 -2
- package/node_modules/@oxygen/shared/dist/plan-band.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/plan-band.js +17 -0
- package/node_modules/@oxygen/shared/dist/plan-capabilities.d.ts +4 -2
- package/node_modules/@oxygen/shared/dist/plan-capabilities.js +12 -1
- package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/plan-limits-view.js +3 -0
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +5 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +29 -0
- package/node_modules/@oxygen/shared/dist/product-analytics-events.js +29 -0
- package/node_modules/@oxygen/shared/dist/recipes.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/recipes.js +13 -0
- package/node_modules/@oxygen/shared/dist/sending-domain-faults.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/sending-domain-faults.js +53 -0
- package/node_modules/@oxygen/shared/dist/sending-domain-rollout.d.ts +30 -0
- package/node_modules/@oxygen/shared/dist/sending-domain-rollout.js +75 -0
- package/node_modules/@oxygen/shared/dist/sequence-failures.d.ts +10 -1
- package/node_modules/@oxygen/shared/dist/sequence-failures.js +68 -6
- package/node_modules/@oxygen/shared/dist/sequence-lifecycle.d.ts +34 -3
- package/node_modules/@oxygen/shared/dist/sequence-lifecycle.js +57 -4
- package/node_modules/@oxygen/shared/dist/sequence-template.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/sequence-template.js +0 -0
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +41 -0
- package/node_modules/@oxygen/shared/dist/sequences.js +86 -4
- package/node_modules/@oxygen/shared/dist/ses-identity-policy.d.ts +19 -0
- package/node_modules/@oxygen/shared/dist/ses-identity-policy.js +54 -0
- package/node_modules/@oxygen/shared/dist/skill-play-routing.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/skill-play-routing.js +75 -0
- package/node_modules/@oxygen/shared/dist/skill-plays.generated.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/skill-plays.generated.js +269 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +7 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.js +7 -0
- package/node_modules/@oxygen/shared/dist/teammates.js +2 -1
- package/node_modules/@oxygen/shared/dist/tool-call-arguments.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/tool-call-arguments.js +68 -0
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +15 -1
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +2 -2
- package/node_modules/@oxygen/shared/dist/workspace-placement.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/workspace-placement.js +11 -0
- package/package.json +2 -2
|
@@ -2,6 +2,8 @@ import { findEnrichmentIntentByAlias } from "./enrichment-intents.js";
|
|
|
2
2
|
import { networkMiningEnabled } from "./network-mining.js";
|
|
3
3
|
import { nativeChatEnabled, withNativeChatDiscovery } from "./native-chat-discovery.js";
|
|
4
4
|
import { RECIPE_PRIMITIVES } from "./recipes.js";
|
|
5
|
+
import { matchSkillPlay } from "./skill-play-routing.js";
|
|
6
|
+
export { matchSkillPlay, SKILL_PLAY_TRIGGERS } from "./skill-play-routing.js";
|
|
5
7
|
// One compact semantic index covers every top-level /api/cli endpoint section.
|
|
6
8
|
// It is navigation, never execution: live schemas, prices, readiness, and
|
|
7
9
|
// approval enforcement remain on the exact command/tool selected from here.
|
|
@@ -26,7 +28,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
26
28
|
posture: "workspace_write",
|
|
27
29
|
gatewayTools: ["oxygen_visual_source_create", "oxygen_visual_render_preview", "oxygen_visual_render_get", "oxygen_visual_image_view"],
|
|
28
30
|
gatewayCommands: ["visuals source-create", "visuals render-preview", "visuals render-get", "visuals download"],
|
|
29
|
-
skills: ["oxygen-knowledge"],
|
|
31
|
+
skills: ["oxygen-linkedin-content", "oxygen-knowledge"],
|
|
30
32
|
endpointSections: ["visuals"],
|
|
31
33
|
intentTerms: ["infographic", "gtm flow image", "render html", "visual design", "carousel pages", "svg", "png", "pdf export", "graphic designer"],
|
|
32
34
|
},
|
|
@@ -114,8 +116,8 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
114
116
|
notFor: "Primitive run diagnosis when no support report is needed; inspect Observability first.",
|
|
115
117
|
execution: "Start or continue a hosted support conversation, optionally file structured metadata, and return its deep link; never send a customer reply automatically.",
|
|
116
118
|
posture: "workspace_write",
|
|
117
|
-
gatewayTools: ["oxygen_feedback", "oxygen_support_ticket_create", "oxygen_support_tickets_list", "oxygen_chat_channel", "oxygen_chat_thread_get", "oxygen_chat_thread_create", "oxygen_chat_thread_reply", "oxygen_chat_file_upload", "oxygen_chat_file_get", "oxygen_chat_thread_read"],
|
|
118
|
-
gatewayCommands: ["feedback", "support chat", "support file", "support list", "chat channel", "chat threads get", "chat threads create", "chat threads reply", "chat files upload", "chat files get", "chat threads read"],
|
|
119
|
+
gatewayTools: ["oxygen_feedback", "oxygen_support_ticket_create", "oxygen_support_tickets_list", "oxygen_chat_threads_list", "oxygen_chat_channel", "oxygen_chat_thread_get", "oxygen_chat_thread_create", "oxygen_chat_thread_reply", "oxygen_chat_file_upload", "oxygen_chat_file_get", "oxygen_chat_thread_read"],
|
|
120
|
+
gatewayCommands: ["feedback", "support chat", "support file", "support list", "chat threads list", "chat channel", "chat threads get", "chat threads create", "chat threads reply", "chat files upload", "chat files get", "chat threads read"],
|
|
119
121
|
skills: ["oxygen-feedback"],
|
|
120
122
|
endpointSections: ["feedback", "support", "chat"],
|
|
121
123
|
intentTerms: ["support", "feedback", "bug report", "feature request", "report issue", "ticket", "complaint", "support chat", "support thread", "chat attachment"],
|
|
@@ -298,7 +300,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
298
300
|
primitive: "signals",
|
|
299
301
|
owns: "Durable typed pulse, engagement, and product events resolved onto records.",
|
|
300
302
|
notFor: "Message bodies, post artifacts, or ad-hoc provider webhooks as private automation state.",
|
|
301
|
-
execution: "Read the hosted signal stream and type registry; tag a registered type with tags attach. Feed subscriptions, Sequences, or Workflows.",
|
|
303
|
+
execution: "Read the hosted signal stream and type registry; tag a registered type with tags attach. Feed subscriptions, Sequences, or Workflows. A visit or view the user reports goes on the person with oxygen_crm_activity_log (activity_type website_visit, direction inbound, live), after creating a missing person with oxygen_crm_record_assert (free, no approval); oxygen_crm_signal_record is for a provider webhook.",
|
|
302
304
|
posture: "workspace_write",
|
|
303
305
|
gatewayTools: ["oxygen_signals_list", "oxygen_signals_leads_today", "oxygen_signals_registry", "oxygen_tags_attach"],
|
|
304
306
|
gatewayCommands: ["signals list", "signals leads-today", "signals registry", "tags attach"],
|
|
@@ -375,7 +377,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
375
377
|
gatewayTools: ["oxygen_sequences_send", "oxygen_sequences_create", "oxygen_sequences_enroll", "oxygen_sequences_start", "oxygen_sequences_list", "oxygen_sequences_update", "oxygen_sequences_get", "oxygen_sequences_push_external", "oxygen_sequences_company_pause"],
|
|
376
378
|
gatewayCommands: ["sequences send", "sequences create", "sequences enroll", "sequences start", "sequences list", "sequences update", "sequences get", "sequences push-external", "sequences pause-company"],
|
|
377
379
|
skills: ["oxygen-sequencer"],
|
|
378
|
-
endpointSections: ["senders", "sequencer", "sequences", "spintax", "subscriptions", "suppressions", "voice"],
|
|
380
|
+
endpointSections: ["email-assets", "email-documents", "senders", "sequencer", "sequences", "spintax", "subscriptions", "suppressions", "voice"],
|
|
379
381
|
// "sender identity", "sender profile" and "phone number" are here because a
|
|
380
382
|
// blind-user eval (2026-08-09) asked for "our sender identity" and got routed
|
|
381
383
|
// to workspace-access — "identity" matched the AUTH sense. The nouns a user
|
|
@@ -398,12 +400,12 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
398
400
|
posture: "external_write",
|
|
399
401
|
gatewayTools: ["oxygen_publishing_posts_create", "oxygen_publishing_posts_list", "oxygen_publishing_posts_approve", "oxygen_ugc_get"],
|
|
400
402
|
gatewayCommands: ["publishing posts create", "publishing posts list", "publishing posts approve", "ugc programs list", "ugc programs duplicate", "ugc programs archive"],
|
|
401
|
-
skills: ["oxygen-publishing", "oxygen-ugc"],
|
|
403
|
+
skills: ["oxygen-publishing", "oxygen-linkedin-content", "oxygen-ugc"],
|
|
402
404
|
endpointSections: ["publishing", "ugc"],
|
|
403
405
|
// "remove program" / "delete program" landed on route: null in a blind eval
|
|
404
406
|
// (2026-09-10); a program is a UGC noun here, and its lifecycle verbs must
|
|
405
407
|
// resolve to the group that owns `ugc programs archive|delete|duplicate`.
|
|
406
|
-
intentTerms: ["publish", "publishing", "schedule post", "content calendar", "posting calendar", "approve post", "social publishing", "video cover", "video thumbnail", "cover image", "post preview", "ugc", "creator program", "user generated content", "creator voice", "sponsored creator", "ugc program", "archive program", "delete program", "remove program", "duplicate program", "copy program"],
|
|
408
|
+
intentTerms: ["publish", "publishing", "schedule post", "content calendar", "posting calendar", "approve post", "social publishing", "video cover", "video thumbnail", "cover image", "post preview", "ugc", "creator program", "user generated content", "creator voice", "sponsored creator", "ugc program", "archive program", "delete program", "remove program", "duplicate program", "copy program", "write posts", "write a post", "post ideas", "content pillars", "hook", "linkedin content"],
|
|
407
409
|
},
|
|
408
410
|
{
|
|
409
411
|
id: "workflows",
|
|
@@ -411,7 +413,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
411
413
|
primitive: "workflows",
|
|
412
414
|
owns: "Deterministic hosted automation definitions, versions, triggers, branching, retries, approvals, modes, and durable runs.",
|
|
413
415
|
notFor: "Local cron, client-side execution, adaptive Agent goals, or outreach cadence already owned by Sequences.",
|
|
414
|
-
execution: "Author locally if useful, then schema/lint/apply and call the Workflow on OXYGEN workers; outbound uses internal oxygen.sequence_enroll against an already active bounded Sequence. Start from the graph schema's examples[0] (oxygen_workflows_schema subject graph): a minimal valid manifest whose tool node adds a Table row with oxygen_table_rows_insert. A draft that is
|
|
416
|
+
execution: "Author locally if useful, then schema/lint/apply and call the Workflow on OXYGEN workers; outbound uses internal oxygen.sequence_enroll against an already active bounded Sequence. Start from the graph schema's examples[0] (oxygen_workflows_schema subject graph): a minimal valid manifest whose tool node adds a Table row with oxygen_table_rows_insert. To change existing rows, a tool node runs oxygen_tables_query with filters, a loop goes over steps.<node>.output.rows (body edge source_handle loop_body, plus an edge back to the loop), and the body calls oxygen_rows_update with row_id loop.each.item._row_id and values. A fixed change on a schedule stays a Workflow: draft and lint it rather than offering a formula column instead; a routine is for checks that need judgment. A cron time is in the person's time zone: set trigger.timezone, and ask for it in your first question when you do not know it. To find why a definition fails, lint it as written first. Saving without running is apply with publish false and workflow and trigger status disabled. If apply is refused, nothing was saved: say so once, never call it saved or a draft in OXYGEN, and never offer another way around the refusal (such as writing the rows directly). Then say in plain words what it would do once saved, and include the full manifest below that: show its manifest every time, since it exists only in your reply.",
|
|
415
417
|
posture: "mixed",
|
|
416
418
|
gatewayTools: ["oxygen_workflows_schema", "oxygen_workflows_lint", "oxygen_workflows_apply", "oxygen_workflows_call", "oxygen_workflows_tail"],
|
|
417
419
|
gatewayCommands: ["workflows schema", "workflows lint", "workflows apply", "workflows call", "workflows tail"],
|
|
@@ -459,7 +461,11 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
459
461
|
gatewayCommands: ["companies search plan", "people search plan", "tools search", "find company"],
|
|
460
462
|
skills: ["oxygen-sourcing", "oxygen-enrichment"],
|
|
461
463
|
endpointSections: ["companies", "find", "lead-sourcing", "people", "search", "sourcing", "tools", "verify"],
|
|
462
|
-
|
|
464
|
+
// Contacts at accounts are a people search: "merge two lists then find
|
|
465
|
+
// contacts" scored Records on "merge"/"contact", and "who we should reach at
|
|
466
|
+
// each company ... cost" scored Billing on "cost".
|
|
467
|
+
intentTerms: ["find companies", "find people", "source leads", "build a list", "tam", "prospect", "provider", "tool catalog", "find email", "find phone", "local businesses", "local business", "google maps", "storefronts", "find contacts", "contacts per account", "contacts per company", "contacts at each", "reach at each", "people at each"],
|
|
468
|
+
negativeTerms: ["in my crm", "in our crm", "in the crm", "existing contacts"],
|
|
463
469
|
},
|
|
464
470
|
{
|
|
465
471
|
id: "linkedin-public-research",
|
|
@@ -486,7 +492,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
486
492
|
posture: "mixed",
|
|
487
493
|
gatewayTools: ["oxygen_integrations_list", "oxygen_integrations_connect", "oxygen_integrations_list_actions"],
|
|
488
494
|
gatewayCommands: ["integrations list", "integrations connect", "custom-integrations list"],
|
|
489
|
-
skills: ["oxygen-tables", "oxygen-gtm"],
|
|
495
|
+
skills: ["oxygen-custom-http", "oxygen-tables", "oxygen-gtm"],
|
|
490
496
|
endpointSections: ["custom-integrations", "integrations"],
|
|
491
497
|
intentTerms: ["integration", "integrations", "connect provider", "oauth", "byok", "bring your own key", "provider key", "provider connection", "custom http", "custom integration", "connection readiness"],
|
|
492
498
|
},
|
|
@@ -591,6 +597,19 @@ export function inferCapabilityRoute(query) {
|
|
|
591
597
|
const normalized = normalizeIntent(query);
|
|
592
598
|
if (!normalized)
|
|
593
599
|
return null;
|
|
600
|
+
if (/\b(custom http|custom integrations?|own (?:rest|http) api)\b/.test(normalized)) {
|
|
601
|
+
const integrations = ROUTE_BY_ID.get("connected-integrations");
|
|
602
|
+
if (integrations) {
|
|
603
|
+
const removal = /\b(remov\w*|delet\w*|disconnect\w*|cleanup|clean up|throwaway|disposable)\b/.test(normalized);
|
|
604
|
+
const tools = removal ? ["oxygen_custom_integrations_remove", "oxygen_custom_integrations_list"] : ["oxygen_custom_integrations_list", "oxygen_custom_integrations_apply", "oxygen_custom_integration_connect"];
|
|
605
|
+
const commands = removal ? ["custom-integrations remove", "custom-integrations list"] : ["custom-integrations list", "custom-integrations apply", "custom-integrations connect"];
|
|
606
|
+
return {
|
|
607
|
+
card: { ...integrations, gatewayTools: tools, gatewayCommands: commands, skills: ["oxygen-custom-http"], execution: removal ? "Preview the exact custom HTTP connection and tools, then approve the returned plan_hash. Caller definitions and history remain, but bound callers stop working; provider-side key revocation is separate." : "Read oxygen-custom-http for the manifest schema and auth types. Apply registers catalog tools without a provider call; connect stores credentials separately. Preview a tool request before approved live execution." },
|
|
608
|
+
recommendedTools: tools,
|
|
609
|
+
recommendedCommands: commands,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
}
|
|
594
613
|
if (/\b(mailbox(?:es)?|inbox(?:es)?)\b/.test(normalized) && /\b(standby|reserve|replacement|replace|burn(?:ed|t)?)\b/.test(normalized)) {
|
|
595
614
|
const campaign = /\b(campaign|sequence|cadence)\b/.test(normalized);
|
|
596
615
|
const card = ROUTE_BY_ID.get(campaign ? "sequences" : "sending-infrastructure") ?? ROUTE_BY_PRIMITIVE.get("sequences");
|
|
@@ -624,7 +643,10 @@ export function inferCapabilityRoute(query) {
|
|
|
624
643
|
// Card scoring stays the owner of routing; the catalogue only breaks a
|
|
625
644
|
// no-match tie ("get the funding round for these accounts" names no card
|
|
626
645
|
// term) and decorates the data cards it belongs to.
|
|
627
|
-
|
|
646
|
+
// A feature-skill play the ask matches votes for the cards its skill owns and,
|
|
647
|
+
// when the chosen card lists that skill, travels on the route as the file to open.
|
|
648
|
+
const play = matchSkillPlay(normalized) ?? sourcingPlaybookFor(normalized);
|
|
649
|
+
const card = explicit ?? highestScoringRoute(normalized, play?.skill) ?? (catalogRow ? cardForCatalogueIntent(catalogRow) : null);
|
|
628
650
|
if (!card)
|
|
629
651
|
return null;
|
|
630
652
|
const recommendations = recommendationsFor(card, normalized);
|
|
@@ -634,7 +656,7 @@ export function inferCapabilityRoute(query) {
|
|
|
634
656
|
// Sourcing filters belong to the planner, not per-company enrichment.
|
|
635
657
|
const catalogue = catalogRow && CATALOGUE_CARD_IDS.has(card.id) && !isCompanySourcingIntent(normalized)
|
|
636
658
|
? catalogueRecommendations(catalogRow, card) : null;
|
|
637
|
-
return withRecordBrief({
|
|
659
|
+
return withSkillPlay(withRecordBrief({
|
|
638
660
|
card: routeCardFor(card, normalized),
|
|
639
661
|
recommendedTools: recommendations.tools,
|
|
640
662
|
recommendedCommands: recommendations.commands,
|
|
@@ -643,7 +665,30 @@ export function inferCapabilityRoute(query) {
|
|
|
643
665
|
...(catalogue && catalogRow
|
|
644
666
|
? { catalogIntent: { id: catalogRow.id, label: catalogRow.label, status: catalogRow.status, group: catalogRow.group, tools: catalogue.tools, commands: catalogue.commands, exactCommand: catalogue.exactCommand, exactMcpCall: catalogue.exactMcpCall } }
|
|
645
667
|
: {}),
|
|
646
|
-
}, networkMiningEnabled(runtimeEnv()));
|
|
668
|
+
}, networkMiningEnabled(runtimeEnv())), play);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Contacts at accounts has no play of its own: oxygen-sourcing's SKILL.md sends
|
|
672
|
+
* "merging sources, setting contacts per account" to its playbook reference,
|
|
673
|
+
* so the route points there (2026-10-22 suite: the merge-then-contacts case
|
|
674
|
+
* missed two contacts per account and the second-source rule without it).
|
|
675
|
+
* Contacts already in the CRM stay with Records.
|
|
676
|
+
*/
|
|
677
|
+
function sourcingPlaybookFor(query) {
|
|
678
|
+
return /\b(find contacts|contacts (per|at each) (account|company)|reach at each|people at each)\b/.test(query)
|
|
679
|
+
&& !/\b(crm|existing contacts)\b/.test(query)
|
|
680
|
+
? { skill: "oxygen-sourcing", slug: "playbook", file: "references/playbook.md" }
|
|
681
|
+
: null;
|
|
682
|
+
}
|
|
683
|
+
/** The play rides only on a card that lists its skill, and that skill is named first. */
|
|
684
|
+
function withSkillPlay(route, play) {
|
|
685
|
+
if (!play || !route.card.skills.includes(play.skill))
|
|
686
|
+
return route;
|
|
687
|
+
return {
|
|
688
|
+
...route,
|
|
689
|
+
card: { ...route.card, skills: [play.skill, ...route.card.skills.filter((skill) => skill !== play.skill)] },
|
|
690
|
+
play,
|
|
691
|
+
};
|
|
647
692
|
}
|
|
648
693
|
/**
|
|
649
694
|
* A data-type ask ("tech stack", "decision makers at each account", "funding
|
|
@@ -734,12 +779,14 @@ export const OXYGEN_PRODUCT_SKILL_AUDIENCES = Object.freeze({
|
|
|
734
779
|
"oxygen-deliverability": "workspace",
|
|
735
780
|
"oxygen-sequencer": "workspace",
|
|
736
781
|
"oxygen-publishing": "workspace",
|
|
782
|
+
"oxygen-linkedin-content": "workspace",
|
|
737
783
|
"oxygen-signals": "workspace",
|
|
738
784
|
"oxygen-table-tidy": "workspace",
|
|
739
785
|
"oxygen-diagnostics": "workspace",
|
|
740
786
|
"oxygen-feedback": "workspace",
|
|
741
787
|
"oxygen-clay-migration": "workspace",
|
|
742
788
|
"oxygen-ugc": "workspace",
|
|
789
|
+
"oxygen-custom-http": "workspace",
|
|
743
790
|
// Retired (RETIRED_PRODUCT_SKILLS): readable for existing pins, never served.
|
|
744
791
|
"oxygen-playbooks": "workspace",
|
|
745
792
|
"oxygen-visual-design": "workspace",
|
|
@@ -803,6 +850,8 @@ export function serializeCapabilityRoute(route, surfaceOrOptions = "cli", option
|
|
|
803
850
|
return null;
|
|
804
851
|
const nativeChat = (typeof surfaceOrOptions === "string" ? options.nativeChat : surfaceOrOptions.nativeChat) ?? nativeChatEnabled(runtimeEnv());
|
|
805
852
|
route = withNativeChatDiscovery(route, nativeChat);
|
|
853
|
+
const play = route.play && (isPublicCapabilitySkill(route.play.skill) || visibleSkills?.includes(route.play.skill))
|
|
854
|
+
? route.play : null;
|
|
806
855
|
return {
|
|
807
856
|
capability_id: route.card.id,
|
|
808
857
|
layer: route.card.layer,
|
|
@@ -812,6 +861,15 @@ export function serializeCapabilityRoute(route, surfaceOrOptions = "cli", option
|
|
|
812
861
|
execution: route.card.execution,
|
|
813
862
|
posture: route.card.posture,
|
|
814
863
|
skills: route.card.skills.filter((name) => isPublicCapabilitySkill(name) || visibleSkills?.includes(name)),
|
|
864
|
+
// The play to open before planning, in both read forms like `hydrate` below.
|
|
865
|
+
...(play ? {
|
|
866
|
+
play: {
|
|
867
|
+
skill: play.skill,
|
|
868
|
+
file: play.file,
|
|
869
|
+
read_cli: `oxygen skills get ${play.skill} --file ${play.file} --json`,
|
|
870
|
+
read_mcp: { tool: "oxygen_skills_get", arguments: { name: play.skill, file: play.file } },
|
|
871
|
+
},
|
|
872
|
+
} : {}),
|
|
815
873
|
endpoint_sections: route.card.endpointSections,
|
|
816
874
|
recommended_commands: route.recommendedCommands,
|
|
817
875
|
recommended_tools: route.recommendedTools,
|
|
@@ -1149,6 +1207,20 @@ function readInspectionRecommendation(query) {
|
|
|
1149
1207
|
if (action)
|
|
1150
1208
|
return null;
|
|
1151
1209
|
const stored = positive.replace(/\bwithout\s+(?:changing|approving|rejecting|publishing|running|executing|refreshing)\b(?:\s+(?:widget\s+)?data\b)?/g, "");
|
|
1210
|
+
// A daily cap is context for an inbox inspection, not a recurring job or a
|
|
1211
|
+
// request to change its volume. Keep explicit writes and other owners intact.
|
|
1212
|
+
const mailboxInspection = stored.replace(/\bdaily (?:(?:campaign|sending|email) )?(?:limits?|caps?)\b/g, "sending limit");
|
|
1213
|
+
if (/\b(?:mailbox(?:es)?|inbox(?:es)?|email accounts?|email senders?)\b/.test(stored)
|
|
1214
|
+
&& /\b(?:health|reputation|rejections?|bounc\w*|deliverability|dsn|ndr)\b/.test(stored)
|
|
1215
|
+
&& !isHostedWorkflowIntent(mailboxInspection)
|
|
1216
|
+
&& !isSecondPartyDecisionIntent(stored)
|
|
1217
|
+
&& !/\b(?:linkedin|whatsapp|sequences?|campaigns?|tables?|columns?|crm|posts?|messages?|conversations?|threads?|dns|spf|dkim|dmarc|verify|verification|create|write|edit|update|change|delete|remove|archive|save|approve|reject|publish|run|running|execute|send|refresh|launch|enable|disable|apply|import|sync|schedule|enroll|add|set|pause|resume|test)\b/.test(mailboxInspection))
|
|
1218
|
+
return {
|
|
1219
|
+
capabilityId: "sending-infrastructure",
|
|
1220
|
+
tools: ["oxygen_mailboxes_health", "oxygen_mailboxes_get", "oxygen_mailboxes_deliverability_evidence"],
|
|
1221
|
+
commands: ["mailboxes health", "mailboxes get", "mailboxes deliverability evidence"],
|
|
1222
|
+
execution: "Read stored reputation, measurement freshness and seven-day rejection/send evidence with oxygen_mailboxes_health. Its mailboxes selector accepts inbox ids or addresses and defaults to full evidence; fleet totals and coverage stay unfiltered. CLI: mailboxes health --mailboxes <id-or-address> --json. Read oxygen_mailboxes_get for today's effective cap; deliverability evidence adds stored placement and domain checks. These reads cost 0 credits and do not sync providers or change settings. External MCP uses oxygen_capabilities_read; hydrate the selected tool's schema before calling.",
|
|
1223
|
+
};
|
|
1152
1224
|
// A trigger diagnosis needs the live definition, not run tailing or a call.
|
|
1153
1225
|
// Keep execution, authoring and run-history requests with their own owners.
|
|
1154
1226
|
if (/\bworkflows?\b/.test(stored)
|
|
@@ -1247,8 +1319,11 @@ function isPartnerBonusReadIntent(query) {
|
|
|
1247
1319
|
/** The card a route reports: Publishing's own, or its UGC view for a creator-program ask. */
|
|
1248
1320
|
function routeCardFor(card, query) {
|
|
1249
1321
|
const readOnly = readInspectionRecommendation(query);
|
|
1250
|
-
if (readOnly?.capabilityId === card.id)
|
|
1251
|
-
|
|
1322
|
+
if (readOnly?.capabilityId === card.id) {
|
|
1323
|
+
const skill = preferredSkillForIntent(card, query);
|
|
1324
|
+
return { ...card, posture: "read_only", execution: readOnly.execution,
|
|
1325
|
+
...(skill ? { skills: [skill, ...card.skills.filter(name => name !== skill)] } : {}) };
|
|
1326
|
+
}
|
|
1252
1327
|
const placementGuide = card.id === "sending-infrastructure" ? placementGuideRecommendation(query) : null;
|
|
1253
1328
|
if (placementGuide)
|
|
1254
1329
|
card = { ...card, posture: "read_only", execution: placementGuide.execution };
|
|
@@ -1280,6 +1355,10 @@ function routeCardFor(card, query) {
|
|
|
1280
1355
|
return { ...card, execution: feed.execution };
|
|
1281
1356
|
if (card.primitive === "publishing" && isUgcIntent(query))
|
|
1282
1357
|
return { ...card, ...UGC_ROUTE_VIEW };
|
|
1358
|
+
if (card.primitive === "publishing" && isPostDraftIntent(query))
|
|
1359
|
+
return { ...card, execution: POST_DRAFT_EXECUTION };
|
|
1360
|
+
if (card.primitive === "signals" && isSignalCaptureIntent(query))
|
|
1361
|
+
return { ...card, execution: SIGNAL_CAPTURE_EXECUTION, skills: ["oxygen-signals", "oxygen-crm"] };
|
|
1283
1362
|
const contactFind = card.id === "sourcing-and-provider-tools" ? oneShotContactFindingRecommendation(query) : null;
|
|
1284
1363
|
if (contactFind)
|
|
1285
1364
|
card = { ...card, execution: contactFind.execution };
|
|
@@ -1320,6 +1399,8 @@ function routeCardFor(card, query) {
|
|
|
1320
1399
|
card = { ...card, posture: "read_only", execution: CANONICAL_COPY_READ_EXECUTION };
|
|
1321
1400
|
else if (card.id === "knowledge-graph" && isWorkspaceVoiceIntent(query))
|
|
1322
1401
|
card = { ...card, execution: WORKSPACE_VOICE_EXECUTION };
|
|
1402
|
+
if (card.primitive === "agents" && isAdaptiveWatchIntent(query))
|
|
1403
|
+
card = { ...card, execution: ADAPTIVE_WATCH_EXECUTION };
|
|
1323
1404
|
if (card.primitive === "workflows" && isRecurringCompanyMonitoringIntent(query))
|
|
1324
1405
|
card = {
|
|
1325
1406
|
...card,
|
|
@@ -1361,6 +1442,12 @@ function routeCardFor(card, query) {
|
|
|
1361
1442
|
const removal = card.primitive === "tables" ? tableRowRemovalRecommendation(query) : null;
|
|
1362
1443
|
if (removal)
|
|
1363
1444
|
card = { ...card, posture: "workspace_write", execution: removal.execution };
|
|
1445
|
+
const insert = card.primitive === "tables" ? tableRowInsertRecommendation(query) : null;
|
|
1446
|
+
if (insert)
|
|
1447
|
+
card = { ...card, posture: "workspace_write", execution: insert.execution };
|
|
1448
|
+
const update = card.primitive === "tables" ? tableRowUpdateRecommendation(query) : null;
|
|
1449
|
+
if (update)
|
|
1450
|
+
card = { ...card, posture: "workspace_write", execution: update.execution };
|
|
1364
1451
|
const sent = card.id === "connected-linkedin" ? sentLinkedInInvitationReadRecommendation(query) : null;
|
|
1365
1452
|
if (sent)
|
|
1366
1453
|
card = { ...card, execution: sent.execution };
|
|
@@ -1400,7 +1487,7 @@ function preferredSkillForIntent(card, query) {
|
|
|
1400
1487
|
return "oxygen-enrichment";
|
|
1401
1488
|
if (card.id === "billing-and-budgets" && /\b(explain|why|reduce|increas\w*|burn|spent)\b/.test(query))
|
|
1402
1489
|
return "oxygen-diagnostics";
|
|
1403
|
-
if (card.id === "sending-infrastructure" && /\b(bounc\w*|health|placement|deliverability|spam)\b/.test(query))
|
|
1490
|
+
if (card.id === "sending-infrastructure" && /\b(bounc\w*|health|reputation|rejections?|placement|deliverability|spam)\b/.test(query))
|
|
1404
1491
|
return "oxygen-deliverability";
|
|
1405
1492
|
if (["signals", "linkedin-public-research"].includes(card.id) && /\blinkedin\b/.test(query))
|
|
1406
1493
|
return "oxygen-signals";
|
|
@@ -1519,6 +1606,14 @@ export const CAPABILITY_ROUTE_MCP_CALLS = {
|
|
|
1519
1606
|
sequenceReplyToStop: () => ({ tool: "oxygen_sequences_update", arguments: { sequence: "<sequence>", reply_to_stop_text: "Reply STOP to opt out." } }),
|
|
1520
1607
|
ugcProgramCreate: () => ({ tool: "oxygen_ugc_program_create", arguments: { product_url: "<your product site>" } }),
|
|
1521
1608
|
// The first comment rides in `content`, as the CLI's --first-comment does.
|
|
1609
|
+
publishingPostDraft: () => ({
|
|
1610
|
+
tool: "oxygen_publishing_posts_create",
|
|
1611
|
+
arguments: { content_text: "<post>", publish_at: "<iso>", status: "draft" },
|
|
1612
|
+
}),
|
|
1613
|
+
signalCapture: () => ({
|
|
1614
|
+
tool: "oxygen_crm_activity_log",
|
|
1615
|
+
arguments: { object: "people", row_id: "<person rowId or email>", activity_type: "website_visit", direction: "inbound", summary: "<what they viewed>", occurred_at: "<iso>", mode: "live" },
|
|
1616
|
+
}),
|
|
1522
1617
|
publishingFirstComment: () => ({
|
|
1523
1618
|
tool: "oxygen_publishing_posts_create",
|
|
1524
1619
|
arguments: { content_text: "<post>", publish_at: "<iso>", sender_account_id: "<sender_id>", content: { first_comment: "<comment, e.g. the link>" } },
|
|
@@ -1614,6 +1709,8 @@ function isSequenceUnsubscribeIntent(query) {
|
|
|
1614
1709
|
&& !/\b(unsubscribed|lists?|import\w*|suppress\w*|block ?list|blocklist|do not contact|dnc)\b/.test(query);
|
|
1615
1710
|
}
|
|
1616
1711
|
/** A post's first comment is set with the post; `publishing posts update` takes the same flag. */
|
|
1712
|
+
export const POST_DRAFT_COMMAND = 'oxygen publishing posts create --draft --publish-at <iso> --text "<post>" --json';
|
|
1713
|
+
export const SIGNAL_CAPTURE_COMMAND = 'oxygen crm activity log people <rowId or email> --type website_visit --direction inbound --summary "<what they viewed>" --live --json';
|
|
1617
1714
|
export const PUBLISHING_FIRST_COMMENT_COMMAND = 'oxygen publishing posts create --publish-at <iso> --sender <sender_id> --text "<post>" --first-comment "<comment, e.g. the link>" --json';
|
|
1618
1715
|
/**
|
|
1619
1716
|
* The comment OXYGEN posts right after a post goes out ("put the link in the first
|
|
@@ -1865,6 +1962,41 @@ function tableRowRemovalRecommendation(query) {
|
|
|
1865
1962
|
execution: "Delete Table rows with tables rows remove-selection (oxygen_table_rows_remove_selection): the rows a readable filter tree matches (--filter-tree-json / filter_tree, the same tree tables query takes), exact row_ids, or an all_matching selection with search, sort, range and exclusions. Hydrate its schema first. Unreadable filter nodes and filters arrays are refused; unqualified all_matching deletes every row. This is permanent and costs 0 credits; to keep a restorable copy first, tables duplicate (oxygen_tables_duplicate, 0 credits). Read the matching rows before deletion and the survivors afterward; each removed row's values stay readable with rows history (oxygen_rows_history). Deleting rows does not stop campaign enrollment.",
|
|
1866
1963
|
};
|
|
1867
1964
|
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Literal row values go in with the row-insert tool. Without this card "add rows"
|
|
1967
|
+
* ranked create, enrichment and the external Records import first (sweep 3).
|
|
1968
|
+
*/
|
|
1969
|
+
function tableRowInsertRecommendation(query) {
|
|
1970
|
+
if (!/\brows?\b/.test(query) || !/\b(add|adding|insert\w*|append\w*|put|enter|write)\b/.test(query)
|
|
1971
|
+
|| /\b(delete|remove|update|edit|change|set|mark|existing|import\w*|csv|crm|records?|enrich\w*|sync\w*|workflows?|agents?|campaigns?|sequences?|webhooks?|feeds?|functions?|callables?|cells?|dedup\w*|duplicates?)\b/.test(query)
|
|
1972
|
+
|| /\b(?:add|insert|new)\s+(?:an?\s+|the\s+)?(?:\w+\s+)?columns?\b/.test(query)
|
|
1973
|
+
|| isSecondPartyDecisionIntent(query))
|
|
1974
|
+
return null;
|
|
1975
|
+
return {
|
|
1976
|
+
tools: ["oxygen_table_rows_insert", "oxygen_tables_create", "oxygen_tables_query"],
|
|
1977
|
+
commands: ["tables rows insert", "tables create", "tables query"],
|
|
1978
|
+
execution: "Add literal rows with tables rows insert (oxygen_table_rows_insert): table plus rows, an array of {column_key: value} objects. tables create (oxygen_tables_create) makes the columns but takes no rows, so insert after it. Manual rows are a free internal write. Read the rows back with tables query (oxygen_tables_query).",
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Changing values on rows that already exist is oxygen_rows_update. Without this
|
|
1983
|
+
* card "update rows" / "set a cell" ranked create and enrichment tools, and an
|
|
1984
|
+
* Oxygent concluded no row update existed and offered a formula column instead of
|
|
1985
|
+
* the weekly Workflow it was asked for (System 1 routing case, 2026-10-22).
|
|
1986
|
+
*/
|
|
1987
|
+
function tableRowUpdateRecommendation(query) {
|
|
1988
|
+
if (!/\b(rows?|cells?)\b/.test(query)
|
|
1989
|
+
|| !(/\b(update\w*|edit\w*|chang\w*|sets?|setting|overwrit\w*|mark\w*)\b/.test(query) || /\bexisting (?:table )?rows?\b/.test(query))
|
|
1990
|
+
|| /\b(delete|remove|insert\w*|append\w*|import\w*|csv|crm|records?|enrich\w*|sync\w*|campaigns?|sequences?|functions?|callables?|dedup\w*|duplicates?|history|provenance|rerun|re-run|retry|columns? run|run columns?|formulas?)\b/.test(query)
|
|
1991
|
+
|| /\b(?:add|insert|new|create)\s+(?:an?\s+|the\s+)?(?:\w+\s+)?(?:columns?|rows?)\b/.test(query)
|
|
1992
|
+
|| isSecondPartyDecisionIntent(query))
|
|
1993
|
+
return null;
|
|
1994
|
+
return {
|
|
1995
|
+
tools: ["oxygen_rows_update", "oxygen_tables_query", "oxygen_rows_upsert"],
|
|
1996
|
+
commands: ["tables update", "tables query", "tables upsert"],
|
|
1997
|
+
execution: "Change values on existing rows with tables update (oxygen_rows_update): table, row_id and values as {column_key: value}, one row per call, a free internal write. Find the rows and their _row_id with tables query (oxygen_tables_query) and filters first; tables upsert (oxygen_rows_upsert) writes by a key column instead. In a Workflow, loop over the query's rows and call oxygen_rows_update with row_id loop.each.item._row_id. Read the rows back after writing.",
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
1868
2000
|
/** Reads of grid values/state are separate from executing or resolving actions. */
|
|
1869
2001
|
function tableReadRecommendation(query) {
|
|
1870
2002
|
if (isFunctionIntent(query) || isSecondPartyDecisionIntent(query)
|
|
@@ -2219,6 +2351,12 @@ function explicitCapabilityIntent(query) {
|
|
|
2219
2351
|
return ROUTE_BY_ID.get(readOnly.capabilityId) ?? null;
|
|
2220
2352
|
if (isOxygentChatIntent(query))
|
|
2221
2353
|
return ROUTE_BY_ID.get("agents") ?? null;
|
|
2354
|
+
// Values on existing Table rows, ahead of the sourcing rule whose "prospect"
|
|
2355
|
+
// claimed "set a value on rows where stage is prospect". Scheduled or tag
|
|
2356
|
+
// wording keeps its Workflow and Tags owners.
|
|
2357
|
+
if (tableRowUpdateRecommendation(query)
|
|
2358
|
+
&& !/\b(workflows?|agents?|routines?|automat\w*|schedul\w*|weekly|daily|nightly|hourly|every|cron|tags?|tagg\w*)\b/.test(query))
|
|
2359
|
+
return ROUTE_BY_ID.get("tables") ?? null;
|
|
2222
2360
|
// Importing an AI answer is GEO evidence, rather than a Table file import.
|
|
2223
2361
|
if (/\b(?:geo|aeo|ai (?:(?:search|answer) )?visibility|consumer ai answers?|answer engine optimization|generative engine optimization)\b/.test(query))
|
|
2224
2362
|
return ROUTE_BY_ID.get("geo") ?? null;
|
|
@@ -2334,6 +2472,15 @@ function explicitCapabilityIntent(query) {
|
|
|
2334
2472
|
// claimed "a first comment on my LinkedIn post" and "... automatically".
|
|
2335
2473
|
if (isFirstCommentIntent(query))
|
|
2336
2474
|
return ROUTE_BY_PRIMITIVE.get("publishing") ?? null;
|
|
2475
|
+
// Writing the workspace's own post is a Publishing draft, ahead of the keyword
|
|
2476
|
+
// post search ("a LinkedIn post about ...") and the public LinkedIn read owner,
|
|
2477
|
+
// which claimed "write a LinkedIn post and save it as a draft".
|
|
2478
|
+
if (isPostDraftIntent(query))
|
|
2479
|
+
return ROUTE_BY_PRIMITIVE.get("publishing") ?? null;
|
|
2480
|
+
// One event the user reports ("log that she visited our pricing page") is a
|
|
2481
|
+
// Signal on the person, not a CRM note or a table row.
|
|
2482
|
+
if (isSignalCaptureIntent(query))
|
|
2483
|
+
return ROUTE_BY_PRIMITIVE.get("signals") ?? null;
|
|
2337
2484
|
// Publish/approve are writes even when the noun is a public LinkedIn post.
|
|
2338
2485
|
// The 2026-09-30 connector baseline offered the cookieless read owner first.
|
|
2339
2486
|
if (isPublishingPostIntent(query))
|
|
@@ -2355,6 +2502,8 @@ function explicitCapabilityIntent(query) {
|
|
|
2355
2502
|
if (isMailboxVolumeIntent(query) && !/\b(workflow|automation|automate|webhook|cron)\b/.test(query)) {
|
|
2356
2503
|
return ROUTE_BY_ID.get("sending-infrastructure") ?? null;
|
|
2357
2504
|
}
|
|
2505
|
+
if (isAdaptiveWatchIntent(query))
|
|
2506
|
+
return ROUTE_BY_PRIMITIVE.get("agents") ?? null;
|
|
2358
2507
|
if (isHostedWorkflowIntent(query))
|
|
2359
2508
|
return ROUTE_BY_PRIMITIVE.get("workflows") ?? null;
|
|
2360
2509
|
if (placementGuideRecommendation(query))
|
|
@@ -2531,10 +2680,16 @@ function matchesIntentTerm(query, term) {
|
|
|
2531
2680
|
}
|
|
2532
2681
|
return false;
|
|
2533
2682
|
}
|
|
2534
|
-
|
|
2683
|
+
/**
|
|
2684
|
+
* A matched play's skill counts as one multi-word term for each card that names
|
|
2685
|
+
* it first: "cold email the whole market" matched the whole-market play, yet its
|
|
2686
|
+
* one card term ("email") picked Messages over Sequences.
|
|
2687
|
+
*/
|
|
2688
|
+
const PLAY_SKILL_TERM_SCORE = 6;
|
|
2689
|
+
function highestScoringRoute(query, playSkill) {
|
|
2535
2690
|
let best = null;
|
|
2536
2691
|
for (const card of OXYGEN_CAPABILITY_ROUTES) {
|
|
2537
|
-
let score = 0;
|
|
2692
|
+
let score = playSkill !== undefined && card.skills[0] === playSkill ? PLAY_SKILL_TERM_SCORE : 0;
|
|
2538
2693
|
for (const term of card.intentTerms) {
|
|
2539
2694
|
if (matchesIntentTerm(query, term))
|
|
2540
2695
|
score += term.includes(" ") ? 6 : 2;
|
|
@@ -2561,6 +2716,25 @@ export const LOCAL_BUSINESS_SCRAPE_COMMAND = `oxygen companies search preview --
|
|
|
2561
2716
|
function isLocalBusinessIntent(query) {
|
|
2562
2717
|
return /\b(local business(?:es)?|google maps|maps listings?|storefronts?|near me|(?:dental|medical|law|veterinary) (?:practices?|offices?|clinics?)|dentists?|restaurants?|cafes?|coffee shops?|gyms?|salons?|plumbers?|electricians?|contractors?|hvac|auto repair|real estate agents?|businesses (?:in|near|around) [a-z])\b/.test(query);
|
|
2563
2718
|
}
|
|
2719
|
+
/** Writing or saving the workspace's own post, or asking to post it: a Publishing draft. */
|
|
2720
|
+
function isPostDraftIntent(query) {
|
|
2721
|
+
if (/\b(engagers?|engag(?:ed|ing|ement)|comments?|commenters?|react\w*|harvest|scrape|competitors?|creators?|repl(?:y|ies)|tables?|rows?|workflows?|automat\w*|sequences?|campaigns?)\b/.test(query))
|
|
2722
|
+
return false;
|
|
2723
|
+
if (/\b(find|search|research|monitor|watch)\b/.test(query))
|
|
2724
|
+
return false;
|
|
2725
|
+
return (/\b(write|draft|drafts|drafted|compose|save|saved)\b/.test(query) && /\bposts?\b/.test(query))
|
|
2726
|
+
|| /\bpost (?:it|this|that)\b|\bpost (?:on|to) (?:linkedin|x|twitter)\b/.test(query);
|
|
2727
|
+
}
|
|
2728
|
+
/** Recording one reported event (a visit, a view) on a person: a Signal, logged on the Record. */
|
|
2729
|
+
function isSignalCaptureIntent(query) {
|
|
2730
|
+
return /\b(record|log|logs|logged|track|note|save|add|capture)\b/.test(query)
|
|
2731
|
+
&& /\b(visit(?:s|ed|or)?|viewed|profile views?|signals?)\b/.test(query)
|
|
2732
|
+
// Outreach wording ("set up an email follow-up ... add the people") is a
|
|
2733
|
+
// Sequence for signalled people, not one reported event.
|
|
2734
|
+
&& !/\b(list|show|read|which|who|rank|leads? to contact|registry|search|find|inbox|webhook|automat\w*|workflows?|sequences?|enrich\w*|tables?|columns?|feeds?|follow ups?|campaigns?|cadences?|outreach|emails)\b/.test(query);
|
|
2735
|
+
}
|
|
2736
|
+
const POST_DRAFT_EXECUTION = "Read the voice page first (oxygen_knowledge_page_get, slug voice) and use only numbers a page states. Save the post with oxygen_publishing_posts_create, status draft and no approved flag: it needs no sender, publishes nothing and opens in Publishing for review. A post written only in the reply is not saved. Publishing it is a separate oxygen_publishing_posts_approve after the user says yes; a missing connected sender is the one blocker to name. End by saying it is a draft, not live, with its web_url; if the save is refused, say the post exists only in this chat.";
|
|
2737
|
+
const SIGNAL_CAPTURE_EXECUTION = "Find the person with oxygen_crm_record_search; if there is no record, create it with oxygen_crm_record_assert (identity email, values full_name and company_domain when known; free, no card). Then log the event on that record with oxygen_crm_activity_log: row_id the rowId the assert or search returned (not object.id) or her email, activity_type website_visit (or profile_view, post_reaction, new_follower), direction inbound, occurred_at, a one-line summary such as the page viewed. It then shows in the Signals feed and on the person's timeline. Not a note or a table row; contacting nobody. End by saying it is recorded as a website-visit Signal and where it shows (the record's web_url); if a write is refused, say nothing was recorded.";
|
|
2564
2738
|
function isPublishingPostIntent(query) {
|
|
2565
2739
|
return /\b(posts?|publishing|content calendar)\b/.test(query)
|
|
2566
2740
|
&& /\b(publish|publishing|schedule|scheduled|approve|approval)\b/.test(query)
|
|
@@ -2702,6 +2876,22 @@ function recommendationsFor(card, query) {
|
|
|
2702
2876
|
exactMcpCall: CAPABILITY_ROUTE_MCP_CALLS.ugcProgramCreate(),
|
|
2703
2877
|
};
|
|
2704
2878
|
}
|
|
2879
|
+
if (card.primitive === "publishing" && isPostDraftIntent(query)) {
|
|
2880
|
+
return {
|
|
2881
|
+
tools: ["oxygen_knowledge_page_get", "oxygen_publishing_posts_create", "oxygen_publishing_posts_get"],
|
|
2882
|
+
commands: ["knowledge page get", "publishing posts create", "publishing posts get"],
|
|
2883
|
+
exactCommand: POST_DRAFT_COMMAND,
|
|
2884
|
+
exactMcpCall: CAPABILITY_ROUTE_MCP_CALLS.publishingPostDraft(),
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
if (card.primitive === "signals" && isSignalCaptureIntent(query)) {
|
|
2888
|
+
return {
|
|
2889
|
+
tools: ["oxygen_crm_record_search", "oxygen_crm_record_assert", "oxygen_crm_activity_log", "oxygen_signals_list"],
|
|
2890
|
+
commands: ["crm search", "crm assert", "crm activity log", "signals list"],
|
|
2891
|
+
exactCommand: SIGNAL_CAPTURE_COMMAND,
|
|
2892
|
+
exactMcpCall: CAPABILITY_ROUTE_MCP_CALLS.signalCapture(),
|
|
2893
|
+
};
|
|
2894
|
+
}
|
|
2705
2895
|
if (card.primitive === "publishing" && isFirstCommentIntent(query)) {
|
|
2706
2896
|
return {
|
|
2707
2897
|
tools: ["oxygen_publishing_posts_create", "oxygen_publishing_posts_update", "oxygen_publishing_posts_get"],
|
|
@@ -2917,6 +3107,12 @@ function recommendationsFor(card, query) {
|
|
|
2917
3107
|
const removal = tableRowRemovalRecommendation(query);
|
|
2918
3108
|
if (removal)
|
|
2919
3109
|
return removal;
|
|
3110
|
+
const insert = tableRowInsertRecommendation(query);
|
|
3111
|
+
if (insert)
|
|
3112
|
+
return insert;
|
|
3113
|
+
const update = tableRowUpdateRecommendation(query);
|
|
3114
|
+
if (update)
|
|
3115
|
+
return update;
|
|
2920
3116
|
const read = tableReadRecommendation(query);
|
|
2921
3117
|
if (read)
|
|
2922
3118
|
return read;
|
|
@@ -3224,6 +3420,14 @@ function recommendationsFor(card, query) {
|
|
|
3224
3420
|
commands: ["workflows templates search", "workflows templates describe", "workflows templates preflight", "workflows templates apply"],
|
|
3225
3421
|
};
|
|
3226
3422
|
}
|
|
3423
|
+
// A scheduled change to existing rows: the query and row-update tools the
|
|
3424
|
+
// graph's nodes call rank beside the authoring gateway.
|
|
3425
|
+
if (/\b(rows?|cells?)\b/.test(query) && /\b(updat\w*|edit\w*|chang\w*|sets?|setting|tag\w*|mark\w*|writ\w*)\b/.test(query)
|
|
3426
|
+
&& !/\b(insert\w*|add\w*|append\w*|new rows?|delete\w*|remove\w*)\b/.test(query))
|
|
3427
|
+
return {
|
|
3428
|
+
tools: ["oxygen_workflows_schema", "oxygen_workflows_lint", "oxygen_tables_query", "oxygen_rows_update", "oxygen_workflows_apply"],
|
|
3429
|
+
commands: ["workflows schema", "workflows lint", "tables query", "tables update", "workflows apply"],
|
|
3430
|
+
};
|
|
3227
3431
|
if (isNetNewLinkedInInitiation(query) || /\b(outreach|sequence|enroll|message)\b/.test(query)) {
|
|
3228
3432
|
return {
|
|
3229
3433
|
tools: ["oxygen_workflows_schema", "oxygen_workflows_lint", "oxygen_workflows_apply", "oxygen_tools_get", "oxygen_sequences_list", "oxygen_sequences_start"],
|
|
@@ -3679,6 +3883,20 @@ function isHostedWorkflowIntent(query) {
|
|
|
3679
3883
|
return /\b(nightly|hourly|daily|weekly|every (?:monday|tuesday|wednesday|thursday|friday|saturday|sunday|day|night|week|month))\b/.test(query)
|
|
3680
3884
|
|| (/\bschedule(?:d)?\b/.test(query) && !/\bpost|content\b/.test(query));
|
|
3681
3885
|
}
|
|
3886
|
+
/**
|
|
3887
|
+
* Watching items that change and working out what each one needs is an Agent's
|
|
3888
|
+
* judgment, not a Workflow's fixed rule: "keep an eye on open deals and suggest
|
|
3889
|
+
* what to do next for each" routed to Records reads, and the Oxygent proposed a
|
|
3890
|
+
* routine without saying why (System 1 routing case, 2026-10-22).
|
|
3891
|
+
*/
|
|
3892
|
+
function isAdaptiveWatchIntent(query) {
|
|
3893
|
+
if (/\b(workflows?|webhooks?|cron|deterministic|fixed rule|sequences?|campaigns?|posts?|mailbox\w*|deliverability|bounce\w*)\b/.test(query))
|
|
3894
|
+
return false;
|
|
3895
|
+
return /\b(keeps? an eye on|keeps? tabs on|keeps? track of|watch(?:es|ing)?|monitor(?:s|ing)?|look(?:s|ing)? after)\b/.test(query)
|
|
3896
|
+
&& /\b(suggest\w*|decid\w*|works? out|figures? out|recommend\w*|judg\w*|what to do|next steps?|next moves?)\b/.test(query)
|
|
3897
|
+
&& /\b(each|every|individual\w*|differently|deals?|pipeline|opportunit(?:y|ies)|accounts?)\b/.test(query);
|
|
3898
|
+
}
|
|
3899
|
+
const ADAPTIVE_WATCH_EXECUTION = "Watching items that change and deciding what fits each one is an Agent's job: each run looks at every item and works out what it needs, while a Workflow repeats one fixed rule. Tell the person that in one plain line. Set it up as an ongoing Agent session with per-run and rolling 24-hour credit caps; in an Oxygent chat, propose a routine and put that line in its why. Read the items first so the goal names real fields (for deals: stage, amount, last activity). Size the caps to one check, about 10 credits a run for reading a few dozen records and writing suggestions, and say so when that is above a limit the person gave. Suggestions stay inside OXYGEN (a task or a note); contacting anyone needs its own approval.";
|
|
3682
3900
|
function isRecurringCompanyMonitoringIntent(query) {
|
|
3683
3901
|
return /\b(companies|company|competitors?)\b/.test(query)
|
|
3684
3902
|
&& /\b(monitor(?:ing)?|watch(?:ing|list)?|track(?:ing)?)\b/.test(query)
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
type FailureState = {
|
|
2
|
+
encryptedState: string;
|
|
3
|
+
expiresAt?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function tagClayMigrationFailureState(error: unknown, encryptedState: string, expiresAt?: string): void;
|
|
2
6
|
/** Consume only state attached in this process by the migration adapter. */
|
|
7
|
+
export declare function consumeClayMigrationFailureState(error: unknown): FailureState | null;
|
|
3
8
|
export declare function readClayMigrationFailureState(error: unknown): string | null;
|
|
9
|
+
export {};
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
// Rotated credentials must survive a failed source read, without becoming part
|
|
2
|
-
// of a customer error, operator log or serialized tool result.
|
|
3
1
|
const failureStates = new WeakMap();
|
|
4
|
-
export function tagClayMigrationFailureState(error, encryptedState) {
|
|
2
|
+
export function tagClayMigrationFailureState(error, encryptedState, expiresAt) {
|
|
5
3
|
if (error !== null && typeof error === "object")
|
|
6
|
-
failureStates.set(error, encryptedState);
|
|
4
|
+
failureStates.set(error, { encryptedState, ...(expiresAt === undefined ? {} : { expiresAt }) });
|
|
7
5
|
}
|
|
8
6
|
/** Consume only state attached in this process by the migration adapter. */
|
|
9
|
-
export function
|
|
7
|
+
export function consumeClayMigrationFailureState(error) {
|
|
10
8
|
if (error === null || typeof error !== "object")
|
|
11
9
|
return null;
|
|
12
10
|
const state = failureStates.get(error) ?? null;
|
|
13
11
|
failureStates.delete(error);
|
|
14
12
|
return state;
|
|
15
13
|
}
|
|
14
|
+
export function readClayMigrationFailureState(error) {
|
|
15
|
+
return consumeClayMigrationFailureState(error)?.encryptedState ?? null;
|
|
16
|
+
}
|
|
@@ -115,6 +115,7 @@ export const BROWSER_OPERATIONS = [
|
|
|
115
115
|
return_to: text("Same-origin path to return to after Checkout, for example /home."),
|
|
116
116
|
source: text("Optional upgrade-entry attribution; omit if unknown."),
|
|
117
117
|
blocked: text("Optional capability that led to the upgrade; omit if unknown."),
|
|
118
|
+
allow_promotion_codes: bool({ description: "Show the promotion-code field instead of the first-month offer." }),
|
|
118
119
|
}, ["plan"]),
|
|
119
120
|
operation("billing.confirm", "/api/cli/billing/confirm", "POST", "billing confirm", "oxygen_billing_confirm", "billing", "Confirm a plan-change preview within 15 minutes; approved accepts payment, term and effective date. Unrestricted members allowed; restricted clients denied.", { token: text(), approved: bool() }, ["token", "approved"], { spends: true }),
|
|
120
121
|
operation("billing.portal", "/api/cli/billing/portal", "POST", "billing portal", "oxygen_billing_portal", "billing", "Create a Stripe billing-portal URL. Unrestricted members allowed; restricted clients denied.", { returnUrl: text() }),
|
|
@@ -3,6 +3,41 @@ import { type ContractIssue, type Infer } from "./schema.js";
|
|
|
3
3
|
export declare const CLAY_MIGRATION_ACTIONS: readonly ["connect", "status", "discover", "read", "transfer", "checkpoint", "report", "disconnect"];
|
|
4
4
|
export declare const CLAY_RESOURCE_TYPES: readonly ["workspace", "folder", "workbook", "table", "audience", "workflow", "function", "claygent", "knowledge", "schedule", "campaign"];
|
|
5
5
|
export declare const CLAY_RESOURCE_DISPOSITIONS: readonly ["pending", "copied", "rebuilt", "needs_decision", "unsupported", "inaccessible", "excluded", "failed"];
|
|
6
|
+
type GlobalAudienceScope = {
|
|
7
|
+
entity_type: "people" | "companies" | "deals";
|
|
8
|
+
global: true;
|
|
9
|
+
archived?: true;
|
|
10
|
+
};
|
|
11
|
+
export declare const CLAY_GLOBAL_AUDIENCE_SCOPES: {
|
|
12
|
+
readonly "global-people": {
|
|
13
|
+
readonly entity_type: "people";
|
|
14
|
+
readonly global: true;
|
|
15
|
+
};
|
|
16
|
+
readonly "global-companies": {
|
|
17
|
+
readonly entity_type: "companies";
|
|
18
|
+
readonly global: true;
|
|
19
|
+
};
|
|
20
|
+
readonly "global-deals": {
|
|
21
|
+
readonly entity_type: "deals";
|
|
22
|
+
readonly global: true;
|
|
23
|
+
};
|
|
24
|
+
readonly "global-archived-people": {
|
|
25
|
+
readonly entity_type: "people";
|
|
26
|
+
readonly global: true;
|
|
27
|
+
readonly archived: true;
|
|
28
|
+
};
|
|
29
|
+
readonly "global-archived-companies": {
|
|
30
|
+
readonly entity_type: "companies";
|
|
31
|
+
readonly global: true;
|
|
32
|
+
readonly archived: true;
|
|
33
|
+
};
|
|
34
|
+
readonly "global-archived-deals": {
|
|
35
|
+
readonly entity_type: "deals";
|
|
36
|
+
readonly global: true;
|
|
37
|
+
readonly archived: true;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export declare function clayGlobalAudienceScope(resourceId: string): GlobalAudienceScope | null;
|
|
6
41
|
export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObject<{
|
|
7
42
|
readonly action: {
|
|
8
43
|
type: "string";
|
|
@@ -17,11 +52,13 @@ export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObje
|
|
|
17
52
|
description?: string;
|
|
18
53
|
};
|
|
19
54
|
readonly resource_id: import("./schema.js").StringProperty;
|
|
55
|
+
readonly row_id: import("./schema.js").StringProperty;
|
|
20
56
|
readonly section: {
|
|
21
57
|
type: "string";
|
|
22
|
-
enum: readonly ("columns" | "fields" | "graph" | "metadata" | "record_ids" | "records" | "rows" | "sequence")[];
|
|
58
|
+
enum: readonly ("columns" | "fields" | "graph" | "metadata" | "record_ids" | "records" | "row" | "rows" | "sequence")[];
|
|
23
59
|
description?: string;
|
|
24
60
|
};
|
|
61
|
+
readonly full: import("./schema.js").BooleanProperty;
|
|
25
62
|
readonly path: import("./schema.js").StringProperty;
|
|
26
63
|
readonly text_offset: {
|
|
27
64
|
type: "integer";
|
|
@@ -35,6 +72,7 @@ export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObje
|
|
|
35
72
|
enum: readonly ("companies" | "deals" | "people")[];
|
|
36
73
|
description?: string;
|
|
37
74
|
};
|
|
75
|
+
readonly archived: import("./schema.js").BooleanProperty;
|
|
38
76
|
readonly record_ids: import("./schema.js").ArrayProperty<{
|
|
39
77
|
type: "integer";
|
|
40
78
|
description?: string;
|
|
@@ -69,3 +107,4 @@ export declare const CLAY_MIGRATION_CONTRACT: import("./schema.js").ContractObje
|
|
|
69
107
|
export type ClayMigrationInput = Infer<typeof CLAY_MIGRATION_CONTRACT>;
|
|
70
108
|
export declare function clayMigrationInputIssues(args: Readonly<Record<string, unknown>>): ContractIssue[];
|
|
71
109
|
export declare const CLAY_MIGRATION_OPERATION: OperationSpec<typeof CLAY_MIGRATION_CONTRACT>;
|
|
110
|
+
export {};
|