From c665da91830285dcef4c1a4692fcc616c0053da9 Mon Sep 17 00:00:00 2001 From: whoisasx Date: Wed, 13 May 2026 21:49:01 +0530 Subject: [PATCH] chore: use ao-agent as composio default user --- packages/cli/__tests__/commands/setup.test.ts | 57 ++++++++++++++----- packages/cli/src/lib/composio-setup.ts | 45 +++++++++------ packages/cli/src/lib/config-instruction.ts | 8 +-- .../notifier-composio/src/index.test.ts | 3 +- .../plugins/notifier-composio/src/index.ts | 3 +- .../docs/plugins/notifiers/composio.mdx | 42 +++++++------- 6 files changed, 99 insertions(+), 59 deletions(-) diff --git a/packages/cli/__tests__/commands/setup.test.ts b/packages/cli/__tests__/commands/setup.test.ts index f46e0b3ac..f3d6a5c70 100644 --- a/packages/cli/__tests__/commands/setup.test.ts +++ b/packages/cli/__tests__/commands/setup.test.ts @@ -420,7 +420,7 @@ describe("setup composio command", () => { ); expect(mockComposioConstructorOptions).toEqual([{ apiKey: "ak_interactive" }]); expect(mockConnectedAccountsList).toHaveBeenCalledWith({ - userIds: ["ao-local"], + userIds: ["ao-agent"], toolkitSlugs: ["slack"], statuses: ["ACTIVE"], limit: 25, @@ -437,7 +437,7 @@ describe("setup composio command", () => { plugin: "composio", defaultApp: "slack", composioApiKey: "ak_interactive", - userId: "ao-local", + userId: "ao-agent", channelName: "iamasx", connectedAccountId: "ca_slack_123", }); @@ -461,7 +461,7 @@ describe("setup composio command", () => { await program.parseAsync(["node", "test", "setup", "composio"]); expect(mockAuthConfigsList).toHaveBeenCalledWith({ toolkit: "slack" }); - expect(mockConnectedAccountsLink).toHaveBeenCalledWith("ao-local", "auth_slack_123", { + expect(mockConnectedAccountsLink).toHaveBeenCalledWith("ao-agent", "auth_slack_123", { allowMultiple: true, }); const writtenYaml = mockWriteFileSync.mock.calls[0][1] as string; @@ -537,7 +537,7 @@ describe("setup composio command", () => { defaultApp: "discord", mode: "webhook", webhookUrl: "https://discord.com/api/webhooks/1234567890/webhook-token", - userId: "ao-local", + userId: "ao-agent", toolVersion: "20260429_01", composioApiKey: "ak_interactive", }); @@ -958,7 +958,7 @@ projects: await program.parseAsync(["node", "test", "setup", "composio"]); expect(mockAuthConfigsCreate).not.toHaveBeenCalledWith("gmail", expect.anything()); - expect(mockConnectedAccountsLink).toHaveBeenCalledWith("ao-local", "auth_gmail_send", { + expect(mockConnectedAccountsLink).toHaveBeenCalledWith("ao-agent", "auth_gmail_send", { allowMultiple: true, }); const writtenYaml = mockWriteFileSync.mock.calls[0][1] as string; @@ -1051,7 +1051,7 @@ projects: plugin: "composio", defaultApp: "slack", composioApiKey: "ak_interactive", - userId: "ao-local", + userId: "ao-agent", channelName: "iamasx", connectedAccountId: "ca_slack_123", }); @@ -1059,6 +1059,35 @@ projects: expect(parsed.defaults?.notifiers).toContain("composio-slack"); }); + it("preserves an existing ao-local Composio userId", async () => { + mockReadFileSync.mockReturnValue(` +notifiers: + composio-slack: + plugin: composio + defaultApp: slack + userId: ao-local +`); + const program = createProgram(); + + await program.parseAsync([ + "node", + "test", + "setup", + "composio-slack", + "--api-key", + "ak_test", + "--connected-account-id", + "ca_slack_123", + "--non-interactive", + ]); + + const writtenYaml = mockWriteFileSync.mock.calls[0][1] as string; + const parsed = parseYaml(writtenYaml) as { + notifiers?: Record>; + }; + expect(parsed.notifiers?.["composio-slack"]?.["userId"]).toBe("ao-local"); + }); + it("interactive Composio Discord webhook setup writes the dedicated notifier", async () => { Object.defineProperty(process.stdin, "isTTY", { value: true, configurable: true }); mockClack.select @@ -1084,7 +1113,7 @@ projects: defaultApp: "discord", mode: "webhook", webhookUrl: "https://discord.com/api/webhooks/1234567890/webhook-token", - userId: "ao-local", + userId: "ao-agent", }); expect(parsed.notifiers?.["composio"]).toBeUndefined(); expect(parsed.defaults?.notifiers).toContain("composio-discord"); @@ -1124,7 +1153,7 @@ projects: defaultApp: "discord", mode: "bot", channelId: "1234567890", - userId: "ao-local", + userId: "ao-agent", connectedAccountId: "ca_discord_123", }); expect(writtenYaml).not.toContain("bot-token"); @@ -1175,7 +1204,7 @@ projects: plugin: "composio", defaultApp: "gmail", emailTo: "admin@example.com", - userId: "ao-local", + userId: "ao-agent", connectedAccountId: "ca_gmail_123", }); expect(parsed.notifiers?.["composio"]).toBeUndefined(); @@ -1515,7 +1544,7 @@ projects: defaultApp: "discord", mode: "webhook", webhookUrl: "https://discord.com/api/webhooks/1234567890/webhook-token", - userId: "ao-local", + userId: "ao-agent", toolVersion: "20260429_01", composioApiKey: "ak_test", }); @@ -1557,7 +1586,7 @@ projects: authScheme: "BEARER_TOKEN", credentials: { token: "bot-token" }, }); - expect(mockConnectedAccountsInitiate).toHaveBeenCalledWith("ao-local", "auth_discord_created", { + expect(mockConnectedAccountsInitiate).toHaveBeenCalledWith("ao-agent", "auth_discord_created", { allowMultiple: true, config: { authScheme: "BEARER_TOKEN", @@ -1580,7 +1609,7 @@ projects: defaultApp: "discord", mode: "bot", channelId: "1234567890", - userId: "ao-local", + userId: "ao-agent", connectedAccountId: "ca_discord_123", toolVersion: "20260429_01", composioApiKey: "ak_test", @@ -1783,7 +1812,7 @@ projects: ]); expect(mockAuthConfigsList).toHaveBeenCalledWith({ toolkit: "gmail" }); - expect(mockConnectedAccountsLink).toHaveBeenCalledWith("ao-local", "auth_gmail_send", { + expect(mockConnectedAccountsLink).toHaveBeenCalledWith("ao-agent", "auth_gmail_send", { allowMultiple: true, }); expect(mockWriteFileSync).not.toHaveBeenCalled(); @@ -1877,7 +1906,7 @@ projects: ]); expect(mockAuthConfigsList).not.toHaveBeenCalledWith({ toolkit: "gmail" }); - expect(mockConnectedAccountsLink).toHaveBeenCalledWith("ao-local", "auth_gmail_custom", { + expect(mockConnectedAccountsLink).toHaveBeenCalledWith("ao-agent", "auth_gmail_custom", { allowMultiple: true, }); expect(mockWriteFileSync).not.toHaveBeenCalled(); diff --git a/packages/cli/src/lib/composio-setup.ts b/packages/cli/src/lib/composio-setup.ts index bf5c8a3f5..5385f2e9c 100644 --- a/packages/cli/src/lib/composio-setup.ts +++ b/packages/cli/src/lib/composio-setup.ts @@ -22,11 +22,13 @@ const COMPOSIO_SLACK_NOTIFIER = "composio-slack"; const COMPOSIO_DISCORD_WEBHOOK_NOTIFIER = "composio-discord"; const COMPOSIO_DISCORD_BOT_NOTIFIER = "composio-discord-bot"; const COMPOSIO_MAIL_NOTIFIER = "composio-mail"; +const DEFAULT_COMPOSIO_USER_ID = "ao-agent"; const GMAIL_SEND_TOOL = "GMAIL_SEND_EMAIL"; const COMPOSIO_DASHBOARD_URL = "https://app.composio.dev"; const DISCORD_APP_URL = "https://discord.com/app"; const DISCORD_DEVELOPER_PORTAL_URL = "https://discord.com/developers/applications"; -const DISCORD_WEBHOOK_DOCS_URL = "https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks"; +const DISCORD_WEBHOOK_DOCS_URL = + "https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks"; export class ComposioSetupError extends Error { constructor( @@ -208,7 +210,9 @@ function asStringArray(value: unknown): string[] { return []; } -function resolveComposioRoutingPreset(value: string | undefined): NotifierRoutingPreset | undefined { +function resolveComposioRoutingPreset( + value: string | undefined, +): NotifierRoutingPreset | undefined { try { return resolveRoutingPresetOption(value, "Composio") as NotifierRoutingPreset | undefined; } catch (error) { @@ -298,7 +302,7 @@ function resolveUserId(opts: { userId?: string }, existing: Record, ): Promise { const existingChannel = - stringValue(opts.channel) ?? stringValue(existing["channelName"]) ?? stringValue(existing["channelId"]); + stringValue(opts.channel) ?? + stringValue(existing["channelName"]) ?? + stringValue(existing["channelId"]); printComposioSlackChannelInfo(); while (true) { @@ -2091,7 +2094,11 @@ async function promptManualGmailConnectedAccountId( } try { - const account = await verifyUsableGmailConnectedAccount(client, userId, String(accountId).trim()); + const account = await verifyUsableGmailConnectedAccount( + client, + userId, + String(accountId).trim(), + ); return account.id; } catch (error) { console.log(chalk.yellow(error instanceof Error ? error.message : String(error))); @@ -2171,7 +2178,9 @@ async function listGmailAuthConfigs(client: ComposioSetupClient): Promise !config.toolkit?.slug || config.toolkit.slug.toLowerCase() === GMAIL_TOOLKIT, ); } @@ -2806,9 +2815,7 @@ async function runInteractiveComposioDiscordWebhookSetup( writeComposioDiscordConfig(configPath, resolved); console.log(chalk.green(`✓ Config written to ${configPath}`)); console.log(chalk.green("✓ Discord webhook configured through Composio")); - console.log( - chalk.dim(`Test it with: ao notify test --to ${targetName} --template basic`), - ); + console.log(chalk.dim(`Test it with: ao notify test --to ${targetName} --template basic`)); clack.outro("Composio Discord webhook setup complete."); return "done"; } @@ -2828,7 +2835,7 @@ async function runInteractiveComposioDiscordBotSetup( const existingChannelId = stringValue(opts.channelId) ?? (existingIsBot ? stringValue(existing["channelId"]) : undefined); const existingConnectedAccountId = existingIsBot - ? stringValue(opts.connectedAccountId) ?? stringValue(existing["connectedAccountId"]) + ? (stringValue(opts.connectedAccountId) ?? stringValue(existing["connectedAccountId"])) : stringValue(opts.connectedAccountId); const optionBotToken = stringValue(opts.botToken); const canReplace = await confirmComposioDiscordWebhookConflict( @@ -2897,7 +2904,8 @@ async function runInteractiveComposioDiscordBotSetup( client, userId, channelId, - connectedAccountId ?? (channelId === existingChannelId ? existingConnectedAccountId : undefined), + connectedAccountId ?? + (channelId === existingChannelId ? existingConnectedAccountId : undefined), optionBotToken, ); if (result === "back") { @@ -2986,10 +2994,11 @@ async function runInteractiveComposioGmailSetup( const existingEmailTo = stringValue(opts.emailTo) ?? (existingIsGmail ? stringValue(existing["emailTo"]) : undefined); const existingConnectedAccountId = existingIsGmail - ? stringValue(opts.connectedAccountId) ?? stringValue(existing["connectedAccountId"]) + ? (stringValue(opts.connectedAccountId) ?? stringValue(existing["connectedAccountId"])) : stringValue(opts.connectedAccountId); const existingAuthConfigId = - stringValue(opts.authConfigId) ?? (existingIsGmail ? stringValue(existing["authConfigId"]) : undefined); + stringValue(opts.authConfigId) ?? + (existingIsGmail ? stringValue(existing["authConfigId"]) : undefined); const canReplace = await confirmComposioDiscordWebhookConflict( clack, targetName, diff --git a/packages/cli/src/lib/config-instruction.ts b/packages/cli/src/lib/config-instruction.ts index d3f069453..8099b6e21 100644 --- a/packages/cli/src/lib/config-instruction.ts +++ b/packages/cli/src/lib/config-instruction.ts @@ -131,7 +131,7 @@ notifiers: composio: plugin: composio # Run 'ao setup composio' to connect Slack through Composio - # userId: ao-local + # userId: ao-agent # connectedAccountId: ca_... # channelName: "#agents" # composioApiKey: ak_... # optional; otherwise uses COMPOSIO_API_KEY @@ -142,7 +142,7 @@ notifiers: # defaultApp: discord # mode: webhook # webhookUrl: https://discord.com/api/webhooks/... - # userId: ao-local + # userId: ao-agent # composioApiKey: ak_... # optional; otherwise uses COMPOSIO_API_KEY composio-discord-bot: plugin: composio @@ -150,7 +150,7 @@ notifiers: # defaultApp: discord # mode: bot # channelId: "1234567890" - # userId: ao-local + # userId: ao-agent # connectedAccountId: ca_... # composioApiKey: ak_... # optional; otherwise uses COMPOSIO_API_KEY composio-mail: @@ -158,7 +158,7 @@ notifiers: # Run 'ao setup composio-mail' to connect Gmail through Composio # defaultApp: gmail # emailTo: alerts@example.com - # userId: ao-local + # userId: ao-agent # connectedAccountId: ca_... # composioApiKey: ak_... # optional; otherwise uses COMPOSIO_API_KEY diff --git a/packages/plugins/notifier-composio/src/index.test.ts b/packages/plugins/notifier-composio/src/index.test.ts index beff79047..30499cb89 100644 --- a/packages/plugins/notifier-composio/src/index.test.ts +++ b/packages/plugins/notifier-composio/src/index.test.ts @@ -131,6 +131,7 @@ describe("notifier-composio", () => { expect(mockToolsExecute).toHaveBeenCalledWith( "SLACK_SEND_MESSAGE", expect.objectContaining({ + userId: "ao-agent", arguments: expect.objectContaining({ markdown_text: expect.any(String) }), }), ); @@ -493,7 +494,7 @@ describe("notifier-composio", () => { it("uses mail setup guidance for Gmail connection errors", async () => { mockToolsExecute.mockRejectedValueOnce( - new Error("No connected account found for user ao-local for toolkit gmail"), + new Error("No connected account found for user ao-agent for toolkit gmail"), ); const notifier = create({ diff --git a/packages/plugins/notifier-composio/src/index.ts b/packages/plugins/notifier-composio/src/index.ts index e9ae1f4c4..906c01834 100644 --- a/packages/plugins/notifier-composio/src/index.ts +++ b/packages/plugins/notifier-composio/src/index.ts @@ -51,6 +51,7 @@ const DEFAULT_TOOL_VERSION: Partial> = { const VALID_APPS = new Set(["slack", "discord", "gmail"]); const VALID_DISCORD_MODES = new Set(["webhook", "bot"]); +const DEFAULT_COMPOSIO_USER_ID = "ao-agent"; const GMAIL_SUBJECT = "Agent Orchestrator Notification"; const DISCORD_WEBHOOK_TOOL_SLUG = "DISCORDBOT_EXECUTE_WEBHOOK"; @@ -352,7 +353,7 @@ export function create(config?: Record): Notifier { stringConfig(config, "entityId") ?? process.env.COMPOSIO_USER_ID ?? process.env.COMPOSIO_ENTITY_ID ?? - "ao-local"; + DEFAULT_COMPOSIO_USER_ID; const emailTo = stringConfig(config, "emailTo"); const toolVersion = resolveToolVersion(config, defaultApp); const forceSkipVersionCheck = boolConfig(config, "dangerouslySkipVersionCheck"); diff --git a/website/content/docs/plugins/notifiers/composio.mdx b/website/content/docs/plugins/notifiers/composio.mdx index e361f3857..b6cb166d8 100644 --- a/website/content/docs/plugins/notifiers/composio.mdx +++ b/website/content/docs/plugins/notifiers/composio.mdx @@ -37,7 +37,7 @@ You can also run the scriptable Slack setup directly: ```bash export COMPOSIO_API_KEY=ak_... -ao setup composio-slack --user-id ao-local --channel "#agents" --non-interactive +ao setup composio-slack --user-id ao-agent --channel "#agents" --non-interactive ``` All Composio setup commands accept `--routing-preset urgent-only`, @@ -99,7 +99,7 @@ notifiers: composio: plugin: composio defaultApp: slack # slack | discord | gmail - userId: ao-local # Composio user id + userId: ao-agent # Composio user id connectedAccountId: ca_... # preferred when available channelName: "#agents" # Slack emailTo: alerts@example.com # required when defaultApp=gmail @@ -110,48 +110,48 @@ notifiers: defaultApp: discord mode: webhook webhookUrl: https://discord.com/api/webhooks/... - userId: ao-local + userId: ao-agent composio-discord-bot: plugin: composio defaultApp: discord mode: bot channelId: "1234567890" - userId: ao-local + userId: ao-agent connectedAccountId: ca_... composio-mail: plugin: composio defaultApp: gmail emailTo: alerts@example.com - userId: ao-local + userId: ao-agent connectedAccountId: ca_... composio-slack: plugin: composio defaultApp: slack - userId: ao-local + userId: ao-agent connectedAccountId: ca_... channelName: "#agents" ``` ## Config -| Key | Required | Default | What it does | -| -------------------- | --------------- | ---------------------- | ------------------------------------------------------------------ | -| `defaultApp` | ✓ | `slack` | Which Composio app to target: `slack`, `discord`, or `gmail` | -| `mode` | Discord only | auto | `webhook` uses Discord webhooks; `bot` uses bot channel messages | -| `userId` | optional | `ao-local` | Composio user id for connected-account lookup | -| `entityId` | optional | — | Backward-compatible alias for `userId` | -| `authConfigId` | setup only | — | Existing Composio Gmail auth config used by `--connect` | -| `connectedAccountId` | Slack/Gmail/bot | — | Specific connected account to use; not used for Discord webhook mode | -| `channelName` | for Slack | — | Slack channel name | -| `channelId` | Discord bot | — | Discord channel id for bot mode | -| `webhookUrl` | Discord webhook | — | Discord webhook URL for webhook mode | -| `emailTo` | ✓ when `gmail` | — | Recipient address | -| `composioApiKey` | optional | env `COMPOSIO_API_KEY` | Override the API key from config | -| `toolVersion` | optional | app default | Tool version passed to `@composio/core` | -| `toolVersions` | optional | — | App-specific tool versions, for example `{ slack: "20260508_00" }` | +| Key | Required | Default | What it does | +| -------------------- | --------------- | ---------------------- | -------------------------------------------------------------------- | +| `defaultApp` | ✓ | `slack` | Which Composio app to target: `slack`, `discord`, or `gmail` | +| `mode` | Discord only | auto | `webhook` uses Discord webhooks; `bot` uses bot channel messages | +| `userId` | optional | `ao-agent` | Composio user id for connected-account lookup | +| `entityId` | optional | — | Backward-compatible alias for `userId` | +| `authConfigId` | setup only | — | Existing Composio Gmail auth config used by `--connect` | +| `connectedAccountId` | Slack/Gmail/bot | — | Specific connected account to use; not used for Discord webhook mode | +| `channelName` | for Slack | — | Slack channel name | +| `channelId` | Discord bot | — | Discord channel id for bot mode | +| `webhookUrl` | Discord webhook | — | Discord webhook URL for webhook mode | +| `emailTo` | ✓ when `gmail` | — | Recipient address | +| `composioApiKey` | optional | env `COMPOSIO_API_KEY` | Override the API key from config | +| `toolVersion` | optional | app default | Tool version passed to `@composio/core` | +| `toolVersions` | optional | — | App-specific tool versions, for example `{ slack: "20260508_00" }` | Slack defaults to `20260508_00`; Discord defaults to `20260429_01`; Gmail defaults to `20260506_01`. Discord uses Composio's `discordbot` toolkit for both webhook and bot modes. Webhook mode calls `DISCORDBOT_EXECUTE_WEBHOOK` with the Discord webhook id/token from `webhookUrl`, so no Composio Discord connect link is required. If `mode` is omitted for Discord, AO uses `webhook` when `webhookUrl` is present and `bot` otherwise.