@oxygen-agent/cli 1.638.1 → 1.662.0

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.
@@ -2,12 +2,11 @@
2
2
  * Social channel capability registry.
3
3
  *
4
4
  * Channel, provider rail, and operation support are deliberately independent.
5
- * LinkedIn is the only registered channel today, but callers resolve capabilities
6
- * through this contract instead of branching on `channel === "linkedin"`. Adding a
7
- * channel later means registering a real adapter profile, not adding placeholder UI.
5
+ * Callers resolve capabilities through this contract instead of branching on a
6
+ * channel name. A channel is registered only when a real adapter exists.
8
7
  */
9
- export const SOCIAL_CHANNELS = ["linkedin"];
10
- export const SOCIAL_PROVIDER_RAILS = ["unipile"];
8
+ export const SOCIAL_CHANNELS = ["linkedin", "x"];
9
+ export const SOCIAL_PROVIDER_RAILS = ["unipile", "composio"];
11
10
  export const SOCIAL_OPERATIONS = [
12
11
  "list_owned_posts",
13
12
  "read_post_metrics",
@@ -61,9 +60,9 @@ const LINKEDIN_UNIPILE_CAPABILITIES = {
61
60
  },
62
61
  metrics: {
63
62
  impressions: { status: "unavailable", provider_field: null, reason: NOT_EXPOSED_BY_UNIPILE },
64
- reactions: { status: "available", provider_field: "reaction_count", reason: null },
65
- comments: { status: "available", provider_field: "comment_count", reason: null },
66
- reposts: { status: "available", provider_field: "repost_count", reason: null },
63
+ reactions: { status: "available", provider_field: "reaction_counter", reason: null },
64
+ comments: { status: "available", provider_field: "comment_counter", reason: null },
65
+ reposts: { status: "available", provider_field: "repost_counter", reason: null },
67
66
  saves: { status: "unavailable", provider_field: null, reason: NOT_EXPOSED_BY_UNIPILE },
68
67
  sends: { status: "unavailable", provider_field: null, reason: NOT_EXPOSED_BY_UNIPILE },
69
68
  clicks: { status: "unavailable", provider_field: null, reason: NOT_EXPOSED_BY_UNIPILE },
@@ -74,8 +73,66 @@ const LINKEDIN_UNIPILE_CAPABILITIES = {
74
73
  webhooks: false,
75
74
  },
76
75
  };
76
+ const X_COMPOSIO_CAPABILITIES = {
77
+ version: 1,
78
+ channel: "x",
79
+ provider_rail: "composio",
80
+ operations: {
81
+ list_owned_posts: {
82
+ status: "unavailable",
83
+ source: "provider",
84
+ provider_operation: null,
85
+ external_write: false,
86
+ limitation: "oxygen_published_posts_only",
87
+ },
88
+ read_post_metrics: {
89
+ status: "limited",
90
+ source: "provider",
91
+ provider_operation: "TWITTER_POST_LOOKUP_BY_POST_ID",
92
+ external_write: false,
93
+ limitation: "connected_user_oauth_owned_posts",
94
+ },
95
+ list_comments: {
96
+ status: "unavailable",
97
+ source: "provider",
98
+ provider_operation: null,
99
+ external_write: false,
100
+ limitation: "not_implemented_for_x",
101
+ },
102
+ reply_to_comment: {
103
+ status: "unavailable",
104
+ source: "provider",
105
+ provider_operation: null,
106
+ external_write: true,
107
+ limitation: "not_implemented_for_x",
108
+ },
109
+ },
110
+ metrics: {
111
+ impressions: { status: "available", provider_field: "public_metrics.impression_count", reason: null },
112
+ reactions: { status: "available", provider_field: "public_metrics.like_count", reason: null },
113
+ comments: { status: "available", provider_field: "public_metrics.reply_count", reason: null },
114
+ reposts: { status: "available", provider_field: "public_metrics.retweet_count", reason: null },
115
+ saves: { status: "available", provider_field: "public_metrics.bookmark_count", reason: null },
116
+ sends: { status: "unavailable", provider_field: null, reason: "not_exposed_by_x_post_lookup" },
117
+ clicks: {
118
+ status: "limited",
119
+ provider_field: "non_public_metrics.url_link_clicks",
120
+ reason: "owned_posts_user_context_30_day_window",
121
+ },
122
+ video_views: {
123
+ status: "limited",
124
+ provider_field: "includes.media[].public_metrics.view_count",
125
+ reason: "video_media_only",
126
+ },
127
+ },
128
+ ingestion: {
129
+ polling: true,
130
+ webhooks: false,
131
+ },
132
+ };
77
133
  const SOCIAL_CAPABILITY_PROFILES = [
78
134
  LINKEDIN_UNIPILE_CAPABILITIES,
135
+ X_COMPOSIO_CAPABILITIES,
79
136
  ];
80
137
  export function listSocialCapabilityProfiles() {
81
138
  return SOCIAL_CAPABILITY_PROFILES;
@@ -83,3 +140,7 @@ export function listSocialCapabilityProfiles() {
83
140
  export function getSocialCapabilityProfile(channel, providerRail) {
84
141
  return SOCIAL_CAPABILITY_PROFILES.find((profile) => profile.channel === channel && profile.provider_rail === providerRail) ?? null;
85
142
  }
143
+ /** Resolve the first configured rail that can perform an operation for a channel. */
144
+ export function getSocialCapabilityProfileForOperation(channel, operation) {
145
+ return SOCIAL_CAPABILITY_PROFILES.find((profile) => profile.channel === channel && profile.operations[operation].status !== "unavailable") ?? null;
146
+ }
@@ -1,6 +1,6 @@
1
1
  import type { TagColor } from "./select-options.js";
2
2
  /** Primitive kinds that participate in the workspace tag union today. */
3
- export declare const TAG_KINDS: readonly ["knowledge_page", "publishing_post", "sequence", "table", "workflow", "recipe", "conversation", "mailbox", "sender", "record", "domain", "project", "engagement_watch"];
3
+ export declare const TAG_KINDS: readonly ["knowledge_page", "publishing_post", "sequence", "table", "workflow", "recipe", "conversation", "mailbox", "sender", "record", "domain", "project"];
4
4
  export type TagKind = (typeof TAG_KINDS)[number];
5
5
  /**
6
6
  * The taggable kinds in prose, for CLI `--help` and MCP tool descriptions.
@@ -8,7 +8,7 @@ export type TagKind = (typeof TAG_KINDS)[number];
8
8
  * Lives next to {@link TAG_KINDS} because those descriptions are the *discovery*
9
9
  * surface: a blind-user eval (2026-07-21) found the tags help still advertising
10
10
  * the original seven kinds, so a user reading `tags --help` had no way to learn
11
- * that domains, projects, or engagement watches were taggable at all — they had
11
+ * that domains or projects were taggable at all — they had
12
12
  * to stumble on each owning group's own help. One string, one import, and
13
13
  * `TAG_KIND_PROSE_MARKERS` fails the build/test if a new kind skips it.
14
14
  */
@@ -24,7 +24,6 @@ export const TAG_KINDS = [
24
24
  "record",
25
25
  "domain",
26
26
  "project",
27
- "engagement_watch",
28
27
  ];
29
28
  /**
30
29
  * The taggable kinds in prose, for CLI `--help` and MCP tool descriptions.
@@ -32,13 +31,13 @@ export const TAG_KINDS = [
32
31
  * Lives next to {@link TAG_KINDS} because those descriptions are the *discovery*
33
32
  * surface: a blind-user eval (2026-07-21) found the tags help still advertising
34
33
  * the original seven kinds, so a user reading `tags --help` had no way to learn
35
- * that domains, projects, or engagement watches were taggable at all — they had
34
+ * that domains or projects were taggable at all — they had
36
35
  * to stumble on each owning group's own help. One string, one import, and
37
36
  * `TAG_KIND_PROSE_MARKERS` fails the build/test if a new kind skips it.
38
37
  */
39
38
  export const TAG_KINDS_PROSE = "publishing posts, knowledge pages, sequences, tables, workflows, recipes, " +
40
39
  "inbox conversations, CRM records, mailboxes, sender accounts, sending domains, " +
41
- "projects, and engagement watches";
40
+ "and projects";
42
41
  /**
43
42
  * The phrase each kind must contribute to {@link TAG_KINDS_PROSE}. Typed as a
44
43
  * total map over TagKind, so adding a kind is a compile error until it is named
@@ -57,7 +56,6 @@ export const TAG_KIND_PROSE_MARKERS = {
57
56
  record: "CRM record",
58
57
  domain: "sending domain",
59
58
  project: "project",
60
- engagement_watch: "engagement watch",
61
59
  };
62
60
  export function isTagKind(value) {
63
61
  return typeof value === "string" && TAG_KINDS.includes(value);
@@ -87,7 +85,6 @@ export const TAG_KIND_LABELS = {
87
85
  record: { one: "CRM record", many: "CRM records" },
88
86
  domain: { one: "domain", many: "domains" },
89
87
  project: { one: "project", many: "projects" },
90
- engagement_watch: { one: "engagement watch", many: "engagement watches" },
91
88
  };
92
89
  /** Inline label for a count: `tagKindLabel("sequence", 1)` → "sequence". */
93
90
  export function tagKindLabel(kind, count) {
@@ -1,3 +1,3 @@
1
- export declare const OXYGEN_VERSION = "1.638.1";
1
+ export declare const OXYGEN_VERSION = "1.662.0";
2
2
  export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.181.0";
3
3
  export declare const MANAGED_INBOX_MINIMUM_CLI_VERSION = "1.326.2";
@@ -1,4 +1,4 @@
1
- export const OXYGEN_VERSION = "1.638.1";
1
+ export const OXYGEN_VERSION = "1.662.0";
2
2
  // The GLOBAL CLI compatibility floor: the oldest CLI allowed to call any
3
3
  // operational route. Raising it hard-rejects every older CLI from the entire
4
4
  // product, so it obeys one law, enforced by scripts/ci/cli-min-version-gate.mjs:
@@ -111,6 +111,11 @@
111
111
  "import": "./dist/tags.js",
112
112
  "default": "./dist/tags.js"
113
113
  },
114
+ "./collab": {
115
+ "types": "./dist/collab.d.ts",
116
+ "import": "./dist/collab.js",
117
+ "default": "./dist/collab.js"
118
+ },
114
119
  "./schedule-label": {
115
120
  "types": "./dist/schedule-label.d.ts",
116
121
  "import": "./dist/schedule-label.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxygen-agent/cli",
3
- "version": "1.638.1",
3
+ "version": "1.662.0",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",