@oxygen-agent/cli 1.1011.405 → 1.1011.446

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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +8 -4
  3. package/dist/connectors-commands.js +3 -0
  4. package/dist/contract-flags.js +22 -9
  5. package/dist/customer-operation-commands.d.ts +5 -0
  6. package/dist/customer-operation-commands.js +101 -0
  7. package/dist/database-query-flags.d.ts +2 -0
  8. package/dist/database-query-flags.js +6 -0
  9. package/dist/help.d.ts +3 -1
  10. package/dist/help.js +33 -0
  11. package/dist/http-client.d.ts +2 -0
  12. package/dist/http-client.js +41 -1
  13. package/dist/index.js +470 -174
  14. package/dist/knowledge-repository-commands.js +12 -14
  15. package/dist/mailbox-ramp-notice.d.ts +11 -0
  16. package/dist/mailbox-ramp-notice.js +53 -0
  17. package/dist/settings-commands.js +5 -4
  18. package/dist/skills.d.ts +5 -4
  19. package/dist/skills.js +106 -79
  20. package/dist/tool-catalog-detail-command.d.ts +7 -0
  21. package/dist/tool-catalog-detail-command.js +19 -0
  22. package/dist/tool-catalog-search-window.d.ts +6 -0
  23. package/dist/tool-catalog-search-window.js +12 -0
  24. package/dist/transcript.js +1 -0
  25. package/node_modules/@oxygen/shared/dist/bounded-read-result.d.ts +1 -1
  26. package/node_modules/@oxygen/shared/dist/bounded-read-result.js +2 -2
  27. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +17 -1
  28. package/node_modules/@oxygen/shared/dist/capability-discovery.js +359 -34
  29. package/node_modules/@oxygen/shared/dist/connection-approval.d.ts +2 -0
  30. package/node_modules/@oxygen/shared/dist/connection-approval.js +2 -0
  31. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.d.ts +2 -0
  32. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +110 -0
  33. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.d.ts +4 -0
  34. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.js +39 -0
  35. package/node_modules/@oxygen/shared/dist/contracts/crm-enrichment-read.d.ts +21 -0
  36. package/node_modules/@oxygen/shared/dist/contracts/crm-enrichment-read.js +15 -0
  37. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.d.ts +2 -0
  38. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +131 -0
  39. package/node_modules/@oxygen/shared/dist/contracts/database.d.ts +30 -0
  40. package/node_modules/@oxygen/shared/dist/contracts/database.js +29 -0
  41. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.d.ts +43 -0
  42. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.js +20 -0
  43. package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.d.ts +12 -0
  44. package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.js +8 -0
  45. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +48 -2
  46. package/node_modules/@oxygen/shared/dist/contracts/operations.js +67 -7
  47. package/node_modules/@oxygen/shared/dist/contracts/table-grid-query.d.ts +4 -0
  48. package/node_modules/@oxygen/shared/dist/contracts/table-grid-query.js +6 -0
  49. package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.d.ts +21 -0
  50. package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.js +27 -0
  51. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-detail.d.ts +19 -0
  52. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-detail.js +10 -0
  53. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-search-window.d.ts +12 -0
  54. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-search-window.js +7 -0
  55. package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.d.ts +1 -0
  56. package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.js +12 -0
  57. package/node_modules/@oxygen/shared/dist/email-campaign-ramp.d.ts +104 -0
  58. package/node_modules/@oxygen/shared/dist/email-campaign-ramp.js +250 -0
  59. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +1 -2
  60. package/node_modules/@oxygen/shared/dist/plan-limits.js +2 -2
  61. package/node_modules/@oxygen/shared/dist/provider-input-value.d.ts +5 -0
  62. package/node_modules/@oxygen/shared/dist/provider-input-value.js +10 -0
  63. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  64. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  65. package/node_modules/@oxygen/shared/package.json +3 -6
  66. package/package.json +1 -1
  67. package/node_modules/@oxygen/shared/dist/copilot-skills.d.ts +0 -15
  68. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +0 -31
  69. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +0 -41
  70. package/node_modules/@oxygen/shared/dist/copilot-skills.js +0 -6
@@ -49,7 +49,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
49
49
  notFor: "Executing GTM work or loading full manifests and schemas before a capability is selected.",
50
50
  execution: "Search by outcome, choose the owner, then hydrate one exact command, MCP schema, provider descriptor, or skill. For instance discovery, read the map and then the primitive that owns the row.",
51
51
  posture: "read_only",
52
- gatewayTools: ["oxygen_capabilities_search", "oxygen_tools_search", "oxygen_recipes_list", "oxygen_workspace_map"],
52
+ gatewayTools: ["oxygen_capabilities_search", "oxygen_skills_get", "oxygen_tools_search", "oxygen_recipes_list", "oxygen_workspace_map"],
53
53
  gatewayCommands: ["capabilities search", "commands search", "skills search", "tools search", "recipes list", "workspace map"],
54
54
  skills: ["oxygen-quickstart", "oxygen-gtm"],
55
55
  // `workspace` (the map) belongs here rather than beside `home`: capability
@@ -73,7 +73,7 @@ export const OXYGEN_CAPABILITY_ROUTES = [
73
73
  skills: ["oxygen-onboarding", "oxygen-quickstart", "oxygen-gtm"],
74
74
  // `activation` remains an exact legacy API family for saved clients, but it
75
75
  // has no recommended gateway and is not an intent term or first-touch path.
76
- endpointSections: ["activation", "copilot", "home"],
76
+ endpointSections: ["activation", "copilot", "home", "onboarding"],
77
77
  // "next step" alone is deliberately absent: it is a phrase every primitive's
78
78
  // users say ("next step of this workflow run"), and a 6-point multi-word hit
79
79
  // would drag those queries here. The terms kept are the ones only a founder
@@ -84,15 +84,15 @@ export const OXYGEN_CAPABILITY_ROUTES = [
84
84
  id: "billing-and-budgets",
85
85
  layer: "Control",
86
86
  primitive: null,
87
- owns: "Credits, plan state, usage receipts, subscriptions, and standing budget policies.",
87
+ owns: "Credits, customer credit prices, plan state, usage receipts, subscriptions, and standing budget policies.",
88
88
  notFor: "Authorizing a specific provider call or external write; that action still needs its own preview and approval.",
89
89
  execution: "Read billing state or set a hosted budget policy; exact action ceilings remain primitive-owned.",
90
90
  posture: "mixed",
91
- gatewayTools: ["oxygen_billing_balance", "oxygen_billing_usage", "oxygen_billing_estimate", "oxygen_budget_list"],
92
- gatewayCommands: ["billing balance", "billing usage", "billing estimate", "budget list", "billing invoices"],
91
+ gatewayTools: ["oxygen_billing_balance", "oxygen_billing_usage", "oxygen_billing_estimate", "oxygen_budget_list", "oxygen_customer_credit_prices"],
92
+ gatewayCommands: ["billing balance", "billing usage", "billing estimate", "budget list", "billing invoices", "pricing customer-credit-prices"],
93
93
  skills: ["oxygen-gtm", "oxygen-diagnostics"],
94
- endpointSections: ["billing", "budget", "limits"],
95
- intentTerms: ["billing", "credits", "credit balance", "usage", "spend", "cost", "cost estimate", "monthly cost", "budget", "subscription", "invoice", "vat", "vat number", "tax id", "cancel plan", "change plan", "upgrade", "downgrade"],
94
+ endpointSections: ["billing", "budget", "limits", "pricing"],
95
+ intentTerms: ["billing", "credits", "credit balance", "customer credit prices", "credit price catalog", "usage", "spend", "cost", "cost estimate", "monthly cost", "budget", "subscription", "invoice", "vat", "vat number", "tax id", "cancel plan", "change plan", "upgrade", "downgrade"],
96
96
  },
97
97
  {
98
98
  id: "support-and-feedback",
@@ -112,15 +112,15 @@ export const OXYGEN_CAPABILITY_ROUTES = [
112
112
  id: "platform-operations",
113
113
  layer: "Control",
114
114
  primitive: null,
115
- owns: "Staff/admin diagnostics, worker queue health, tenant repair, and database operating surfaces.",
116
- notFor: "Ordinary customer GTM work or product Observability over customer-visible runs.",
117
- execution: "Use the authenticated hosted operational endpoints; production mutations and migrations keep their human gates.",
115
+ owns: "Workspace queue health, failed jobs, queue recovery, and workspace database status, attach, migrations, read-only SQL and retired cost-policy controls.",
116
+ notFor: "Customer run timelines or approvals (Observability), Table grid queries (Tables), and external Supabase imports (Integrations).",
117
+ execution: "Inspect the active workspace's queue and database status; repair stale queue state or provision its database through the native scoped controls.",
118
118
  posture: "mixed",
119
- gatewayTools: ["oxygen_worker_queue_stats", "oxygen_worker_failures", "oxygen_observability_runs"],
120
- gatewayCommands: ["worker queue-stats", "worker failures", "db status"],
119
+ gatewayTools: ["oxygen_worker_queue_stats", "oxygen_worker_failures", "oxygen_workspace_queue_repair", "oxygen_workspace_database_status", "oxygen_workspace_database_provision"],
120
+ gatewayCommands: ["worker queue-stats", "worker failures", "worker repair", "db status", "db provision"],
121
121
  skills: ["oxygen-diagnostics"],
122
- endpointSections: ["admin", "db", "worker"],
123
- intentTerms: ["admin", "worker", "queue", "tenant repair", "database", "db status", "stuck jobs", "operations"],
122
+ endpointSections: ["db", "worker"],
123
+ intentTerms: ["worker", "queue", "queue repair", "database", "database hosting", "db status", "stuck jobs", "operations"],
124
124
  },
125
125
  {
126
126
  // Control-layer interaction state, deliberately NOT a sixteenth primitive
@@ -256,11 +256,11 @@ export const OXYGEN_CAPABILITY_ROUTES = [
256
256
  notFor: "Net-new LinkedIn initiation, cadence, enrollment, sender rotation, or reply-stop programs; those belong to Sequences.",
257
257
  execution: "Preview and approve a direct email or existing-thread reply; every sent fact stays in the hosted Messages ledger.",
258
258
  posture: "external_write",
259
- gatewayTools: ["oxygen_inbox_send", "oxygen_email_send", "oxygen_messages_query"],
260
- gatewayCommands: ["inbox send", "email send", "messages query"],
259
+ gatewayTools: ["oxygen_inbox_send", "oxygen_email_send", "oxygen_messages_query", "oxygen_unibox_metadata"],
260
+ gatewayCommands: ["inbox send", "email send", "messages query", "unibox metadata"],
261
261
  skills: ["oxygen-unibox"],
262
- endpointSections: ["email", "inbox", "message-reviews", "messages"],
263
- intentTerms: ["message", "messages", "reply", "email", "inbox", "conversation", "thread", "direct email", "draft reply", "one off"],
262
+ endpointSections: ["email", "inbox", "message-reviews", "messages", "unibox"],
263
+ intentTerms: ["message", "messages", "reply", "email", "inbox", "unibox", "inbox metadata", "conversation", "thread", "direct email", "draft reply", "one off"],
264
264
  },
265
265
  {
266
266
  id: "posts",
@@ -646,7 +646,81 @@ export function inferPrimitiveRoute(query) {
646
646
  return null;
647
647
  return route;
648
648
  }
649
- export function serializeCapabilityRoute(route, surface = "cli") {
649
+ /** Current hosted instruction audiences, shared by discovery and Agent snapshots. */
650
+ export const OXYGEN_PRODUCT_SKILL_AUDIENCES = Object.freeze({
651
+ "oxygen-onboarding": "workspace",
652
+ "oxygen-gtm": "workspace",
653
+ "oxygen-recipes": "workspace",
654
+ "oxygen-playbooks": "workspace",
655
+ "oxygen-linkedin-marketing": "workspace",
656
+ "oxygen-agency-directory": "agency_partner",
657
+ "oxygen-collaboration": "workspace",
658
+ "oxygen-quickstart": "workspace",
659
+ "oxygen-workflow-authoring": "workspace",
660
+ "oxygen-knowledge": "workspace",
661
+ "oxygen-visual-design": "workspace",
662
+ "oxygen-unibox": "workspace",
663
+ "oxygen-email-infra": "workspace",
664
+ "oxygen-deliverability": "workspace",
665
+ "oxygen-sequencer": "workspace",
666
+ "oxygen-table-tidy": "workspace",
667
+ "oxygen-diagnostics": "workspace",
668
+ "oxygen-feedback": "workspace",
669
+ "oxygen-clay-migration": "workspace",
670
+ "oxygen-ugc": "workspace",
671
+ });
672
+ export function canReadProductSkillInstructions(name, entitlements) {
673
+ if (entitlements.authenticated !== true)
674
+ return false;
675
+ const audience = Object.hasOwn(OXYGEN_PRODUCT_SKILL_AUDIENCES, name)
676
+ ? OXYGEN_PRODUCT_SKILL_AUDIENCES[name]
677
+ : undefined;
678
+ return audience === "workspace" || (audience === "agency_partner" && entitlements.agencyPartner);
679
+ }
680
+ /** Identify hosted instruction bodies by native tool lineage, never by user prose. */
681
+ export function readProductSkillInstructionResult(capabilityName, result) {
682
+ if (capabilityName !== "oxygen_skills_get")
683
+ return null;
684
+ const pending = [result];
685
+ for (let i = 0; i < pending.length && i < 24; i += 1) {
686
+ let value = pending[i];
687
+ if (typeof value === "string") {
688
+ try {
689
+ value = JSON.parse(value);
690
+ }
691
+ catch {
692
+ continue;
693
+ }
694
+ }
695
+ if (value === null || typeof value !== "object" || Array.isArray(value))
696
+ continue;
697
+ const envelope = value;
698
+ if (envelope.ok === false || envelope.isError === true || envelope.status === "error")
699
+ continue;
700
+ if (typeof envelope.content === "string") {
701
+ const name = typeof envelope.name === "string" ? envelope.name : "";
702
+ return { slug: Object.hasOwn(OXYGEN_PRODUCT_SKILL_AUDIENCES, name) ? name : null };
703
+ }
704
+ for (const key of ["result", "data", "structuredContent", "structured"]) {
705
+ if (envelope[key] !== undefined)
706
+ pending.push(envelope[key]);
707
+ }
708
+ if (Array.isArray(envelope.content)) {
709
+ for (const block of envelope.content) {
710
+ if (block && typeof block === "object" && block.type === "text" && typeof block.text === "string")
711
+ pending.push(block.text);
712
+ }
713
+ }
714
+ }
715
+ return null;
716
+ }
717
+ /** Public action cards never advertise invite-only or unknown skill instructions. */
718
+ export function isPublicCapabilitySkill(name) {
719
+ return Object.hasOwn(OXYGEN_PRODUCT_SKILL_AUDIENCES, name) && OXYGEN_PRODUCT_SKILL_AUDIENCES[name] === "workspace";
720
+ }
721
+ export function serializeCapabilityRoute(route, surfaceOrOptions = "cli", options = {}) {
722
+ const surface = typeof surfaceOrOptions === "string" ? surfaceOrOptions : "cli";
723
+ const visibleSkills = typeof surfaceOrOptions === "string" ? options.visibleSkills : surfaceOrOptions.visibleSkills;
650
724
  if (!route)
651
725
  return null;
652
726
  return {
@@ -657,7 +731,7 @@ export function serializeCapabilityRoute(route, surface = "cli") {
657
731
  not_for: route.card.notFor,
658
732
  execution: route.card.execution,
659
733
  posture: route.card.posture,
660
- skills: route.card.skills,
734
+ skills: route.card.skills.filter((name) => isPublicCapabilitySkill(name) || visibleSkills?.includes(name)),
661
735
  endpoint_sections: route.card.endpointSections,
662
736
  recommended_commands: route.recommendedCommands,
663
737
  recommended_tools: route.recommendedTools,
@@ -682,6 +756,8 @@ export function serializeCapabilityRoute(route, surface = "cli") {
682
756
  hydrate: {
683
757
  cli: "oxygen commands get <exact-command> --json",
684
758
  mcp: "oxygen_capabilities_schema",
759
+ skill_cli: "oxygen skills get <selected-skill> --json",
760
+ skill_mcp: { tool: "oxygen_skills_get", arguments: { name: "<selected-skill>" } },
685
761
  },
686
762
  };
687
763
  }
@@ -774,23 +850,75 @@ function routeCardFor(card, query) {
774
850
  if (card.primitive === "publishing" && isUgcIntent(query))
775
851
  return { ...card, ...UGC_ROUTE_VIEW };
776
852
  if (card.id === "knowledge-graph" && isWorkspaceVoiceIntent(query))
777
- return { ...card, execution: WORKSPACE_VOICE_EXECUTION };
853
+ card = { ...card, execution: WORKSPACE_VOICE_EXECUTION };
778
854
  if (card.primitive === "workflows" && isRecurringCompanyMonitoringIntent(query))
779
- return {
855
+ card = {
780
856
  ...card,
781
857
  execution: "Workflows own recurring execution. Inspect oxygen_blueprints_list, describe and preflight before authoring; a matching Blueprint is not guaranteed. Retain canonical company identities and facts in Records, and time-stamped observations in Signals. Use oxygen_crm_objects_list and oxygen_signals_list to inspect those destinations. Do not apply or enable automation before the required approval and credit scope.",
782
858
  };
783
859
  if (card.primitive === "tables" && isTableCapacityReadIntent(query))
784
- return {
860
+ card = {
785
861
  ...card,
786
862
  execution: "Read oxygen_limits_show for Tables capacity, largest_table and largest_table_rows. Use that returned table reference with oxygen_tables_query and a small limit to inspect sample rows; no enrichment run is needed.",
787
863
  };
788
864
  if (card.primitive === "tables" && isFunctionIntent(query))
789
- return {
865
+ card = {
790
866
  ...card,
791
867
  execution: "List, describe or inspect Function runs without executing. Draft edits are isolated; publish requires the inspected draft hash and changes future invocations. Bind adds a caller column for free; columns run previews cost before a bounded approved run. MCP uses the mixed-effect oxygen_callables_manage through oxygen_capabilities_call, including its read operations.",
792
868
  };
793
- return card;
869
+ if (card.primitive === "tables" && isPlainTableIntent(query))
870
+ card = {
871
+ ...card,
872
+ execution: "Create a workspace-private Table or inspect its schema and rows. Manual rows, column labels, visibility and member favorites cost 0 credits. Change only the display label with columns update (oxygen_columns_update); columns rename (oxygen_columns_rename) requires a new stable key. Favorite one table or folder with tables favorite (oxygen_tables_favorite), then read tables favorites (oxygen_tables_favorites). For tidy guidance, read oxygen-table-tidy with skills get (oxygen_skills_get); tables tidy-suggest (oxygen_tables_tidy_suggest) analyzes without changes. Read the owning schema, apply the requested scope, then describe or query the Table to verify it.",
873
+ };
874
+ const maintenance = card.primitive === "tables" ? tableMaintenanceRecommendation(query) : null;
875
+ if (maintenance)
876
+ card = { ...card, execution: maintenance.execution };
877
+ const read = card.primitive === "tables" ? tableReadRecommendation(query) : null;
878
+ if (read)
879
+ card = { ...card, execution: read.execution };
880
+ const database = card.id === "platform-operations" ? databaseRecommendation(query) : null;
881
+ if (database)
882
+ card = { ...card,
883
+ owns: "Active-workspace PostgreSQL hosting, attachment, migrations, read-only SQL and retired cost-policy status.",
884
+ notFor: "Provider credentials, enrichment, workspace billing, or staff-wide database operations.",
885
+ execution: database.execution,
886
+ };
887
+ const specializedSkill = preferredSkillForIntent(card, query);
888
+ return specializedSkill ? { ...card, skills: [specializedSkill, ...card.skills.filter((skill) => skill !== specializedSkill)] } : card;
889
+ }
890
+ /** Search surfaces share the selected skill owner; a general router is fallback. */
891
+ function preferredSkillForIntent(card, query) {
892
+ if (card.id === "tables") {
893
+ if (/\bclay\b/.test(query) && /\b(import|export|migrat\w*|mov\w*)\b/.test(query))
894
+ return "oxygen-clay-migration";
895
+ if (isTableMaintenanceIntent(query))
896
+ return "oxygen-table-tidy";
897
+ if (/\b(stuck|failed|failure|trace|provenance|diagnos\w*)\b/.test(query))
898
+ return "oxygen-diagnostics";
899
+ if (isLinkedInProfileWatcherIntent(query))
900
+ return "oxygen-linkedin-marketing";
901
+ }
902
+ if (card.id === "billing-and-budgets" && /\b(explain|why|reduce|increas\w*|burn|spent)\b/.test(query))
903
+ return "oxygen-diagnostics";
904
+ if (card.id === "sending-infrastructure" && /\b(bounc\w*|health|placement|deliverability|spam)\b/.test(query))
905
+ return "oxygen-deliverability";
906
+ if (["signals", "linkedin-public-research"].includes(card.id) && /\blinkedin\b/.test(query))
907
+ return "oxygen-linkedin-marketing";
908
+ return null;
909
+ }
910
+ function isTableMaintenanceIntent(query) {
911
+ return /\b(tables?|columns?|folders?)\b/.test(query)
912
+ && !/\b(functions?|callables?|campaigns?|tags?|workflows?|agents?)\b/.test(query)
913
+ && (/\b(tidy|tidying|cleanup|clean up|reorder|formatting|duplicates?|favou?rites?|unfavou?rite|star|unstar)\b/.test(query)
914
+ || (/\bcolumns?\b/.test(query) && /\b(hide|hidden|rename|renaming|label|key)\b/.test(query)));
915
+ }
916
+ function isPlainTableIntent(query) {
917
+ if (isTableCapacityReadIntent(query))
918
+ return false;
919
+ return isTableMaintenanceIntent(query) || (/\btable\b/.test(query)
920
+ && /\b(create|manual|sample|private)\b/.test(query)
921
+ && !/\b(enrich\w*|classif\w*|research|prompt|sourc\w*|import|linkedin|function|callable)\b/.test(query));
794
922
  }
795
923
  function isFunctionIntent(query) {
796
924
  return /\b(functions?|callables?)\b/.test(query) && !/\bformula functions?\b/.test(query);
@@ -909,6 +1037,19 @@ function isSequenceScheduleIntent(query) {
909
1037
  return /\bsend(?:ing)? (?:schedule|window)s?\b/.test(query)
910
1038
  && !/\b(workflow|automation|automate|webhook|cron)\b/.test(query);
911
1039
  }
1040
+ // How much campaign email one inbox may send: its daily campaign limit and the
1041
+ // ramp-up that climbs to it. The 2026-10-01 MCP baseline searched "mailbox
1042
+ // sending settings warmup ramp daily limit" and was routed to Workflows (bare
1043
+ // "daily" reads as a schedule); "ramp up a new inbox" went to Messages. LinkedIn,
1044
+ // WhatsApp and call limits keep their own owners, and provider warm-up without
1045
+ // campaign volume words keeps the card's warm-up path.
1046
+ function isMailboxVolumeIntent(query) {
1047
+ if (/\b(linkedin|whatsapp|calls?|dial\w*|voice)\b/.test(query))
1048
+ return false;
1049
+ const inbox = /\b(mailbox\w*|inbox(?:es)?|email accounts?|sending accounts?|sender accounts?)\b/.test(query);
1050
+ const volume = /\b(ramp\w*|daily (?:campaign )?(?:limit|cap)s?|sending limits?|send limits?|(?:emails?|sends?) (?:per|a) day|volume)\b/.test(query);
1051
+ return inbox && volume;
1052
+ }
912
1053
  // The unsubscribe option on a Sequence's emails, not the list of people who
913
1054
  // opted out: a list, import or do-not-contact ask stays with suppressions.
914
1055
  function isSequenceUnsubscribeIntent(query) {
@@ -1031,21 +1172,29 @@ function isConnectedAppSpendIntent(query) {
1031
1172
  * when the pages are wiki, LinkedIn, web, PDF or result pages.
1032
1173
  */
1033
1174
  function isWorkspacePagesIntent(query) {
1034
- // "Sales Nav" is LinkedIn Sales Navigator.
1035
- if (/\bsales ?nav/.test(query))
1175
+ // A row, column or cell is content inside a page, and Sales Nav is LinkedIn.
1176
+ if (/\bsales ?nav/.test(query) || /\b(rows?|columns?|cells?|filters?|duplicates?)\b/.test(query))
1036
1177
  return false;
1037
- if (/\b(side ?bars?|side ?nav\w*|side ?menus?|nav ?bars?|nav|navigation|left (?:menu|nav\w*|rail|panel)|main menu)\b/.test(query))
1178
+ const navigation = /\b(side ?bars?|side ?nav\w*|side ?menus?|nav ?bars?|nav|navigation|left (?:menu|nav\w*|rail|panel)|main menu)\b/.test(query);
1179
+ const visibility = /\b(hid(?:e|es|den|ing)|unhide|visib\w*|invisible)\b/.test(query);
1180
+ const showInNavigation = /\bshow(?:s|n|ing)?\b/.test(query)
1181
+ && /\b(?:in|on|from|to)\s+(?:the |our |my |this )?(?:side ?bar|side ?nav\w*|navigation|nav|left (?:menu|nav\w*|rail|panel))\b/.test(query);
1182
+ // Explicit sidebar visibility may name the page's primitive: hide Knowledge
1183
+ // from the sidebar or show Tables in navigation still means Customise.
1184
+ if (navigation && (visibility || showInNavigation))
1038
1185
  return true;
1039
- // "Menu items" alone is as likely a restaurant's menu as the app's.
1040
- if (/\bmenu items?\b/.test(query) && /\b(hid(?:e|es|den|ing)|unhide|show(?:s|n|ing)?|visib\w*|customi[sz]\w*)\b/.test(query))
1186
+ if (/\bmenu items?\b/.test(query) && /\b(hid(?:e|es|den|ing)|unhide|show(?:s|n|ing)?|visib\w*)\b/.test(query))
1187
+ return true;
1188
+ // Content and navigation within a primitive keep that primitive's owner.
1189
+ // Check this before generic "customize pages" or a navigation noun.
1190
+ if (/\b(wiki|knowledge|linkedin|compan(?:y|ies)|tables?|landing|web ?pages?|websites?|carousels?|pdfs?|paginat\w*|next page|per page|page size|results?|dashboards?)\b/.test(query))
1191
+ return false;
1192
+ if (navigation)
1041
1193
  return true;
1042
1194
  if (/\bcustomi[sz](?:e|es|ed|ing|ation)\b/.test(query) && /\b(pages?|menus?)\b/.test(query))
1043
1195
  return true;
1044
- // The web page's own name: Settings → Customise.
1045
1196
  if (/^customi[sz]e$|\bsettings?\W{1,5}customi[sz]e\b/.test(query))
1046
1197
  return true;
1047
- if (/\b(wiki|linkedin|compan(?:y|ies)|landing|web ?pages?|websites?|carousels?|pdfs?|paginat\w*|next page|per page|page size|results?|dashboards?|rows?|columns?)\b/.test(query))
1048
- return false;
1049
1198
  return /\bpages?\b/.test(query) && /\b(hid(?:e|es|den|ing)|unhide|show(?:s|n|ing)?|visib\w*|invisible)\b/.test(query);
1050
1199
  }
1051
1200
  /**
@@ -1073,7 +1222,119 @@ function isCompanySourcingIntent(query) {
1073
1222
  return /\b(companies|businesses|firms|company search|company sourcing)\b/.test(query)
1074
1223
  && /\b(search|plan|find|source|sourcing|discover|build|tam|prospect)\b/.test(query);
1075
1224
  }
1225
+ /** Reads of grid values/state are separate from executing or resolving actions. */
1226
+ function tableReadRecommendation(query) {
1227
+ if (!/\btables?\b/.test(query) || isFunctionIntent(query) || isSecondPartyDecisionIntent(query)
1228
+ || /\b(workflows?|agents?|actions?|items?|retry|resolve|effects?|imports?|webhooks?|feeds?|enrich\w*|create|add|insert|upsert|update|delete|remove|rename|hide|archive|columns? run)\b/.test(query)
1229
+ || /\b(run|execute|start|cancel)\s+(?:(?:the|my|these)\s+)?(?:columns?|table|enrichment)\b/.test(query))
1230
+ return null;
1231
+ if (/\bcells?\b/.test(query) && /\b(history|changes?|provenance)\b/.test(query))
1232
+ return {
1233
+ tools: ["oxygen_tables_cell_history", "oxygen_tables_cell_inspect"], commands: ["cells history", "cells inspect"],
1234
+ execution: "Read one cell's recorded value changes with cells history; cells inspect adds its provider attempt timeline. These reads do not run columns or resolve an uncertain external effect.",
1235
+ };
1236
+ if (/\bcells?\b/.test(query) && /\b(provider|attempts?|timeline)\b/.test(query))
1237
+ return {
1238
+ tools: ["oxygen_tables_cell_inspect", "oxygen_tables_cell_history"], commands: ["cells inspect", "cells history"],
1239
+ execution: "Inspect one cell's final value and provider attempt timeline with cells inspect. Cell history reads recorded changes; neither executes a column or records a verified external outcome.",
1240
+ };
1241
+ if (isTableCapacityReadIntent(query) && !/\bgrid\b/.test(query))
1242
+ return null;
1243
+ if (!/\b(grid|preview|raw rows|fill rates?|next page|pagination|cursor)\b/.test(query)
1244
+ && !(/\b(read|show|inspect|query)\b/.test(query) && /\brows?\b/.test(query)))
1245
+ return null;
1246
+ if (/\b(filtered?|filters?|sorts?|search|crm)\b/.test(query) && /\b(grid|counts?|registry)\b/.test(query))
1247
+ return {
1248
+ tools: ["oxygen_table_rows_page", "oxygen_tables_query", "oxygen_tables_preview"], commands: ["tables rows page", "tables query", "tables preview"],
1249
+ execution: "Read the native rows page for grid filters/sorts/search and count modes. exact counts the matching rows; estimate uses reconciled registry metadata only for an unfiltered scope, otherwise counts exactly. registry never counts and returns no total for a narrowed or unavailable registry scope; skip returns no total. Follow nextCursor with the same table, filters, search, sort and CRM scope; do not substitute an offset. Canonical tables query reads raw values; format=grid refuses those filters/projections.",
1250
+ };
1251
+ if (/\b(preview|fill rates?|without row|summary only|summary_only)\b/.test(query))
1252
+ return {
1253
+ tools: ["oxygen_tables_preview", "oxygen_tables_query", "oxygen_table_fill_progress"], commands: ["tables preview", "tables query", "tables fill-progress"],
1254
+ execution: "Use tables preview for compact rows, optional include_cell_states and whole-table fill stats; summary_only omits row JSON. rowCount is exact, rates may be sampled and summary.statsSampled labels that scope. Follow its nextCursor with the same options. tables query reads raw values or format=grid previews/state. tables fill-progress separately reports ingestion progress/failure; none of these runs columns.",
1255
+ };
1256
+ return {
1257
+ tools: ["oxygen_tables_query", "oxygen_tables_preview", "oxygen_table_rows_page", "oxygen_table_fill_progress"], commands: ["tables query", "tables preview", "tables rows page", "tables fill-progress"],
1258
+ execution: 'Read tables query raw values, or format="grid" for summarized cell previews and per-cell state. Grid defaults to 50 rows and caps at 200; pass grid.nextCursor as cursor with the same table/limit. Grid refuses row_id, fields, filters/filterTree, search, sorts, formula_values and nonzero offset; it is not a snapshot. Its first page uses the reconciled registry count when available, otherwise exact; continuation skips counting (null total), labeled rowCountMode. include_stats adds optional whole-table column stats/count; unavailable stats are omitted. include_fill only adds optional fill state when the first page is empty; later/nonempty pages omit it. tables preview supplies fill-rate summaries; tables fill-progress reads ingestion status. These reads do not execute columns.',
1259
+ };
1260
+ }
1261
+ /** Native database work outranks generic workspace access and credit billing. */
1262
+ function databaseRecommendation(query) {
1263
+ if (!/\b(databases?|postgres(?:ql)?|db|sql)\b/.test(query)
1264
+ || /\b(tables?|grid|external|import|workflows?|agents?|enrich\w*|providers?|integrations?|oauth|byok|mailbox\w*|crm|hubspot|salesforce|supabase)\b/.test(query))
1265
+ return null;
1266
+ const actions = [
1267
+ ["oxygen_workspace_database_status", "db status"], ["oxygen_database_cost_policy", "db cost-policy"],
1268
+ ["oxygen_database_query", "db query"], ["oxygen_database_attach", "db attach"],
1269
+ ["oxygen_database_migrate", "db migrate"], ["oxygen_workspace_database_provision", "db provision"],
1270
+ ["oxygen_database_cost_policy_reconcile", "db reconcile-cost-policy"],
1271
+ ];
1272
+ const first = /\b(migrat\w*|rotate credentials)\b/.test(query) ? 4
1273
+ : /\b(attach|connect|connection)\b/.test(query) ? 3
1274
+ : /\b(query|sql|select|explain)\b/.test(query) ? 2
1275
+ : /\b(provision\w*)\b/.test(query) ? 5
1276
+ : /\breconcile\b/.test(query) ? 6
1277
+ : /\b(cost policy|cost controls?|sleep|suspend|reconcile)\b/.test(query) && !/\bstatus\b/.test(query) ? 1 : 0;
1278
+ const ordered = [actions[first], ...actions.filter((_, index) => index !== first)];
1279
+ return {
1280
+ tools: ordered.map(([tool]) => tool), commands: ordered.map(([, command]) => command),
1281
+ execution: "Inspect db status and db cost-policy for the active workspace. Hosting is included; Neon cost controls are retired, and reconcile-cost-policy is a compatibility no-op, not PostgreSQL sleep control. db query accepts one read-only SELECT/WITH/EXPLAIN statement: request a small max_rows (hard cap 1000). Writes are refused; truncated labels the returned row window, and it has no continuation cursor. Attach (database_url), migrate (optional rotate_credentials) and provision are immediate native writes with no preview or approved field. They preserve workspace identity/access; restricted clients can read status/cost/query but cannot configure. Built-in approval policy is separate from these HTTP fields.",
1282
+ };
1283
+ }
1284
+ /** Direct Table operations keep their native gateway ahead of general authoring.
1285
+ * Explicit Workflow triggers, mailbox imports and CRM attachments retain their owners. */
1286
+ function tableMaintenanceRecommendation(query) {
1287
+ if (isFunctionIntent(query) || isSecondPartyDecisionIntent(query)
1288
+ || /\b(workflows?|agents?|campaigns?|tags?|mailbox\w*|crm|attachments?|chatgpt|connected apps?)\b/.test(query)
1289
+ || (isTableMaintenanceIntent(query) && /\b(skills?|instructions?|guidance)\b/.test(query)))
1290
+ return null;
1291
+ const table = /\btables?\b/.test(query);
1292
+ const result = (tools, commands, execution) => ({ tools, commands, execution });
1293
+ if (/\b(favou?rites?|starred|star|unstar)\b/.test(query) && /\b(tables?|folders?)\b/.test(query)) {
1294
+ const mutate = !/\bstar unstar\b/.test(query) && (/\b(unstar|unfavou?rite)\b/.test(query) || (/\b(favou?rite|star)\b/.test(query) && !/\b(show|list|read|see|favorites|favourites|star unstar)\b/.test(query)));
1295
+ return result(mutate ? ["oxygen_tables_favorite", "oxygen_tables_favorites"] : ["oxygen_tables_favorites", "oxygen_tables_favorite"], mutate ? ["tables favorite", "tables favorites"] : ["tables favorites", "tables favorite"], "Read your member-specific Table/folder stars, then change exactly one table or folder with favorite=true or false. This costs 0 credits. Tool-catalog favorites are separate.");
1296
+ }
1297
+ if (table && /\b(webhooks?|inbound.*endpoint|push.*endpoint|endpoint.*(?:inbound|push))\b/.test(query)) {
1298
+ if (/\bdeliver\w*\b/.test(query))
1299
+ return result(["oxygen_table_webhook_deliveries", "oxygen_table_webhook_get"], ["tables webhook deliveries", "tables webhook get"], "Inspect this Table endpoint and its delivery ledger. A direct Table webhook lands rows; a Workflow webhook starts a published graph.");
1300
+ if (/\b(change|edit|update|rotat\w*|pause|disable|clear)\b/.test(query) && !/\bcreate\b/.test(query))
1301
+ return result(["oxygen_table_webhook_update", "oxygen_table_webhook_get"], ["tables webhook update", "tables webhook get"], "Inspect the endpoint, then update it by endpoint_id. auth_mode=secret rotates the secret and returns it once. auto_run is standing permission; delivery ceilings are explicit or plan-default. Preserve required authorization.");
1302
+ if (/\b(create|add|new|inbound)\b/.test(query) && !/\b(inspect|show|list|read)\b/.test(query))
1303
+ return result(["oxygen_table_webhook_create", "oxygen_table_webhooks_list"], ["tables webhook create", "tables webhook list"], "Create a direct inbound Table endpoint to insert or upsert rows. Inspect configured columns before setting auto_run: it is standing permission, with explicit or plan-default delivery ceilings. Workflows own graph triggers.");
1304
+ return result(["oxygen_table_webhooks_list", "oxygen_table_webhook_get", "oxygen_table_webhook_deliveries"], ["tables webhook list", "tables webhook get", "tables webhook deliveries"], "Inspect direct Table endpoints and their deliveries. Use oxygen_table_webhook_create/update for changes; auth_mode=secret rotates protection. Workflows own graph triggers.");
1305
+ }
1306
+ if (table && /\bfeeds?\b/.test(query)) {
1307
+ return /\b(bind|attach|create|new)\b/.test(query)
1308
+ ? result(["oxygen_feeds_bind", "oxygen_feeds_list"], ["feeds bind", "feeds list"], "Bind the native Table feed: a declared-schema push source or scheduled provider pull. Inspect source, cadence and standing credit scope first; a pull feed is not an inbound webhook endpoint.")
1309
+ : result(["oxygen_feeds_list", "oxygen_feeds_get"], ["feeds list", "feeds get"], "Inspect the native Table feed's source, cadence, caps and state. Pull feeds refill Tables on their own schedule. The current MCP catalog has no general cadence-update tool; do not substitute a Workflow or generic HTTP call.");
1310
+ }
1311
+ const file = /\b(csv|jsonl?|xlsx|excel|spreadsheet|staged|uploaded|file)\b/.test(query)
1312
+ || /\b(?:pending|stuck|abandoned) imports?\b|\bimports?\b.{0,30}\b(pending|stuck|recover\w*)\b/.test(query);
1313
+ if (file && /\b(import\w*|ingestion|upload\w*|preview|columns|sample|chunks)\b/.test(query)) {
1314
+ if (/\b(pending|leased|failed|recover|chunks)\b/.test(query))
1315
+ return result(/\b(chunks|items)\b/.test(query) ? ["oxygen_table_ingestions_items", "oxygen_table_ingestions_get"] : ["oxygen_table_ingestions_get", "oxygen_table_ingestions_items"], ["table-ingestions get", "table-ingestions items"], "Inspect the durable file ingestion and its chunks. Fix the cause before retry-failed; pending or leased chunks are not verified external outcomes. recover_pending polls existing BetterContact requests, not file imports.");
1316
+ if (/\b(url|https?)\b/.test(query))
1317
+ return result(["oxygen_tables_import_url"], ["tables import"], "Use the native public-file URL preview/import tool through the call gateway even for preview. Importing into an existing Table can fire its standing import auto-run; preview and new unarmed targets do not authorize later paid or external actions.");
1318
+ if (/\b(preview|inspect|columns|sample|sheet)\b/.test(query))
1319
+ return result(["oxygen_tables_import_preview", "oxygen_tables_import_staged"], ["tables import-preview", "tables import-staged"], "Inspect the owned staged file's worksheet, complete columns and sample before importing. Keep the selected sheet/mapping intact. Import into an existing Table can fire its standing import auto-run; preview grants no spend authority.");
1320
+ if (/\b(prepare|upload)\b/.test(query))
1321
+ return result(["oxygen_tables_import_upload", "oxygen_tables_import_preview", "oxygen_tables_import_staged"], ["tables import-upload", "tables import-preview", "tables import-staged"], "Prepare an organization-owned upload, transfer bytes to the returned presigned URL, then inspect and import that storage key. Preparing the upload creates no Table/run. Public URL imports use oxygen_tables_import_url.");
1322
+ return result(["oxygen_tables_import_staged", "oxygen_tables_import_preview"], ["tables import-staged", "tables import-preview"], "Import the inspected owned staged file with its selected worksheet/mapping. Existing targets can fire standing import auto-run; a new target has no pre-existing armed columns. Preserve exact authorization and downstream credit/effect guards.");
1323
+ }
1324
+ if (table && /\b(actions?|cells?|items?|runs?)\b/.test(query)) {
1325
+ if (/\b(verified|confirmed)\b/.test(query) && /\b(record|resolve)\b/.test(query) && !/\b(inspect|list|show|read)\b/.test(query))
1326
+ return /\bcells?\b/.test(query)
1327
+ ? result(["oxygen_table_cells_resolve", "oxygen_table_runs_effect_unknown"], ["cells resolve", "table-runs effect-unknown"], "After verifying the external destination, record applied or not_applied by Table cell coordinates. This records an outcome without repeating the effect; retry remains a separate guarded action.")
1328
+ : result(["oxygen_table_action_item_resolve", "oxygen_table_runs_effect_unknown"], ["table-actions resolve", "table-runs effect-unknown"], "After verifying the external destination, record applied or not_applied by action item_id. This records an outcome without repeating the effect; retry remains a separate guarded action.");
1329
+ if (/\b(effect.*unknown|uncertain|verified outcome|blocked.*resolve)\b/.test(query))
1330
+ return result(["oxygen_table_runs_effect_unknown", "oxygen_table_runs_get", "oxygen_table_runs_items"], ["table-runs effect-unknown", "table-runs get", "table-runs items"], "Inspect exact effect-unknown items and verify the external destination. Only then record a verdict with oxygen_table_action_item_resolve by item_id or oxygen_table_cells_resolve by coordinates. Pending/failed items are not proof; retry is separate.");
1331
+ return result(["oxygen_table_action_runs_list", "oxygen_table_runs_get", "oxygen_table_runs_items"], ["table-runs list", "table-runs get", "table-runs items"], "Find the Table's durable action run, then inspect its status and items. Pending, leased and failed are distinct from effect-unknown; do not record a verified verdict or retry an external effect without evidence.");
1332
+ }
1333
+ return null;
1334
+ }
1076
1335
  function explicitCapabilityIntent(query) {
1336
+ if (databaseRecommendation(query))
1337
+ return ROUTE_BY_ID.get("platform-operations") ?? null;
1077
1338
  if (isConnectedAppsIntent(query) || isConnectedAppEventsIntent(query) || isConnectedAppSpendIntent(query)) {
1078
1339
  return ROUTE_BY_ID.get("workspace-access") ?? null;
1079
1340
  }
@@ -1082,6 +1343,16 @@ function explicitCapabilityIntent(query) {
1082
1343
  if (isWorkspacePagesIntent(query) || isWorkspaceSettingsIntent(query)) {
1083
1344
  return ROUTE_BY_ID.get("workspace-access") ?? null;
1084
1345
  }
1346
+ if (/\btables?\b/.test(query) && isSecondPartyDecisionIntent(query))
1347
+ return ROUTE_BY_ID.get("collaboration") ?? null;
1348
+ if (tableReadRecommendation(query) || tableMaintenanceRecommendation(query))
1349
+ return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
1350
+ if (/\btables?\b/.test(query) && /\b(navigation|nav|paging|pagination)\b/.test(query)) {
1351
+ return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
1352
+ }
1353
+ if (/\b(unibox|inbox)\b/.test(query) && /\b(metadata|taxonomy|filters)\b/.test(query)) {
1354
+ return ROUTE_BY_ID.get("messages") ?? null;
1355
+ }
1085
1356
  if (/\b(infographic|graphic designer|render html|carousel pages|visual design|gtm flow image)\b/.test(query))
1086
1357
  return ROUTE_BY_ID.get("visual-rendering") ?? null;
1087
1358
  if (isUgcIntent(query))
@@ -1148,6 +1419,9 @@ function explicitCapabilityIntent(query) {
1148
1419
  // hid every Sequences command.
1149
1420
  if (isSequenceScheduleIntent(query) || isSequenceUnsubscribeIntent(query))
1150
1421
  return ROUTE_BY_PRIMITIVE.get("sequences") ?? null;
1422
+ if (isMailboxVolumeIntent(query) && !/\b(workflow|automation|automate|webhook|cron)\b/.test(query)) {
1423
+ return ROUTE_BY_ID.get("sending-infrastructure") ?? null;
1424
+ }
1151
1425
  if (isHostedWorkflowIntent(query))
1152
1426
  return ROUTE_BY_PRIMITIVE.get("workflows") ?? null;
1153
1427
  if (isCompanySourcingIntent(query))
@@ -1229,6 +1503,8 @@ function explicitCapabilityIntent(query) {
1229
1503
  // and the "label" wording fell to the Tags card below.
1230
1504
  if (isRowClassificationIntent(query))
1231
1505
  return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
1506
+ if (isTableMaintenanceIntent(query))
1507
+ return ROUTE_BY_PRIMITIVE.get("tables") ?? null;
1232
1508
  if (/\b(tag|tags|label|labels)\b/.test(query))
1233
1509
  return ROUTE_BY_PRIMITIVE.get("tags") ?? null;
1234
1510
  if (/\b(disposable|working)\b.{0,30}\b(list|dataset|table)\b|\b(prospect|sourcing) table\b/.test(query)) {
@@ -1347,6 +1623,15 @@ function isPublishingPostIntent(query) {
1347
1623
  && !(/\b(find|search|scrape|research)\b/.test(query) && /\b(about|mention|containing)\b/.test(query));
1348
1624
  }
1349
1625
  function recommendationsFor(card, query) {
1626
+ const database = card.id === "platform-operations" ? databaseRecommendation(query) : null;
1627
+ if (database)
1628
+ return database;
1629
+ if (card.id === "billing-and-budgets" && /\b(credit prices|credit price catalog|customer credit)\b/.test(query)) {
1630
+ return { tools: ["oxygen_customer_credit_prices"], commands: ["pricing customer-credit-prices"] };
1631
+ }
1632
+ if (card.id === "messages" && /\b(unibox|inbox)\b/.test(query) && /\b(metadata|taxonomy|filters|accounts|tags)\b/.test(query)) {
1633
+ return { tools: ["oxygen_unibox_metadata"], commands: ["unibox metadata"] };
1634
+ }
1350
1635
  // A program's lifecycle and a creator's voice are UGC work the Publishing card
1351
1636
  // also claims ("archive program", "creator voice"); before these they got the
1352
1637
  // post tools (UGC E2E pass, 2026-09-29).
@@ -1508,6 +1793,12 @@ function recommendationsFor(card, query) {
1508
1793
  ],
1509
1794
  };
1510
1795
  }
1796
+ if (card.id === "sending-infrastructure" && isMailboxVolumeIntent(query)) {
1797
+ return {
1798
+ tools: ["oxygen_mailboxes_set_ramp", "oxygen_mailboxes_set_cap", "oxygen_mailboxes_get"],
1799
+ commands: ["mailboxes ramp set", "mailboxes cap set", "mailboxes get"],
1800
+ };
1801
+ }
1511
1802
  if (card.id === "sending-infrastructure" && isMailboxOnboardingIntent(query)) {
1512
1803
  return {
1513
1804
  tools: [
@@ -1568,6 +1859,12 @@ function recommendationsFor(card, query) {
1568
1859
  };
1569
1860
  }
1570
1861
  if (card.primitive === "tables") {
1862
+ const read = tableReadRecommendation(query);
1863
+ if (read)
1864
+ return read;
1865
+ const maintenance = tableMaintenanceRecommendation(query);
1866
+ if (maintenance)
1867
+ return maintenance;
1571
1868
  if (isTableCapacityReadIntent(query))
1572
1869
  return {
1573
1870
  tools: ["oxygen_limits_show", "oxygen_tables_query", "oxygen_tables_describe"],
@@ -1618,6 +1915,33 @@ function recommendationsFor(card, query) {
1618
1915
  exactMcpCall: { ...CAPABILITY_ROUTE_MCP_CALLS.functionsList(), ...(/\b(favorites|favourites)\b/.test(query) ? { arguments: { operation: "list_functions", filter: "favorites" } } : {}) },
1619
1916
  };
1620
1917
  }
1918
+ if (isTableMaintenanceIntent(query)) {
1919
+ if (/\b(skills?|instructions?|guidance)\b/.test(query))
1920
+ return {
1921
+ tools: ["oxygen_skills_get", "oxygen_tables_tidy_suggest", "oxygen_tables_describe"],
1922
+ commands: ["skills get", "tables tidy-suggest", "tables describe"],
1923
+ exactCommand: "oxygen skills get oxygen-table-tidy --json",
1924
+ exactMcpCall: { tool: "oxygen_skills_get", arguments: { name: "oxygen-table-tidy" } },
1925
+ };
1926
+ if (/\b(favou?rites?|unfavou?rite|star|unstar)\b/.test(query)) {
1927
+ const read = /\b(list|read|show|see)\b/.test(query);
1928
+ return {
1929
+ tools: read ? ["oxygen_tables_favorites", "oxygen_tables_favorite"] : ["oxygen_tables_favorite", "oxygen_tables_favorites"],
1930
+ commands: read ? ["tables favorites", "tables favorite"] : ["tables favorite", "tables favorites"],
1931
+ };
1932
+ }
1933
+ if (/\bcolumns?\b/.test(query) && /\b(rename|renaming|label|key)\b/.test(query)) {
1934
+ const key = /\b(stable key|column key|snake case|rename key)\b/.test(query);
1935
+ return {
1936
+ tools: [key ? "oxygen_columns_rename" : "oxygen_columns_update", "oxygen_tables_describe"],
1937
+ commands: [key ? "columns rename" : "columns update", "tables describe"],
1938
+ };
1939
+ }
1940
+ return {
1941
+ tools: ["oxygen_tables_tidy_suggest", "oxygen_columns_update", "oxygen_tables_describe", "oxygen_skills_get"],
1942
+ commands: ["tables tidy-suggest", "columns update", "tables describe", "skills get"],
1943
+ };
1944
+ }
1621
1945
  if (isLinkedInProfileWatcherIntent(query)) {
1622
1946
  return {
1623
1947
  tools: ["oxygen_tables_watcher"],
@@ -2004,10 +2328,11 @@ function isSecondPartyDecisionIntent(query) {
2004
2328
  const decisionLanguage = /\b(sign ?off|signoff|approval request|request approval|approval gate|go no go)\b/.test(query);
2005
2329
  const clientDecision = /\bclient\b.{0,20}\b(approv\w*|sign ?off|review|decide|decision|login|role)\b/.test(query)
2006
2330
  || /\b(approv\w*|sign ?off|decision)\b.{0,20}\bfrom the client\b/.test(query);
2331
+ const teammateDecision = /\b(teammate|colleague|workspace member)\b.{0,25}\b(approv\w*|sign ?off|review|decid\w*)\b/.test(query);
2007
2332
  const internalThread = /\b(comment|comments|thread)\b/.test(query)
2008
2333
  && /\b(internal|client|teammate|workspace member|discuss)\b/.test(query)
2009
2334
  && !/\b(linkedin|public|published|post|posts|publishing)\b/.test(query);
2010
- return decisionLanguage || clientDecision || internalThread;
2335
+ return decisionLanguage || clientDecision || teammateDecision || internalThread;
2011
2336
  }
2012
2337
  /** An X (Twitter) post format: thread, poll, alt text, who-can-reply, quote, photo tags. */
2013
2338
  function isXPostFormatIntent(query) {
@@ -0,0 +1,2 @@
1
+ /** Shared refusal discriminator; safe to import without the plan/pricing catalog. */
2
+ export declare const CONNECTION_CEILING_ABOVE_DEFAULT_APPROVAL_KIND = "connection_ceiling_above_default";
@@ -0,0 +1,2 @@
1
+ /** Shared refusal discriminator; safe to import without the plan/pricing catalog. */
2
+ export const CONNECTION_CEILING_ABOVE_DEFAULT_APPROVAL_KIND = "connection_ceiling_above_default";
@@ -0,0 +1,2 @@
1
+ import type { OperationSpec } from "./operations.js";
2
+ export declare const BROWSER_OPERATIONS: readonly OperationSpec[];