Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
027aac2
temp-before-merge-codex-2be5-composio
friuns May 17, 2026
7140fd3
Add Composio preview, suggestions, and logout
friuns May 17, 2026
82b9ccc
Attach Composio connector docs from composer
friuns May 17, 2026
f1fd524
Refine Composio composer suggestion targeting
friuns May 17, 2026
f10ab6d
Remove connector trigger after attaching docs
friuns May 17, 2026
5da308c
Move Composio suggestions into composer controls
friuns May 17, 2026
0fc5c38
Open Composio connector panel for disconnected suggestions
friuns May 17, 2026
36dae1f
Match Composio suggestions by exact mentions
friuns May 17, 2026
8b87915
Include multi-word connector token suggestions
friuns May 17, 2026
5abd14c
Match Composio suggestions from previous word
friuns May 17, 2026
b8687ac
Show one Composio connector after controls
friuns May 17, 2026
095c808
Require exact Composio connector matches
friuns May 17, 2026
3af59c7
Match multi-word Composio aliases before active word
friuns May 17, 2026
d32f7b8
Add setting to toggle Composio suggestions
friuns May 17, 2026
9e49869
Fix Composio PR review findings
friuns May 17, 2026
8a4e57f
Attach Composio panel docs on try
friuns May 17, 2026
6d69096
Keep composer text after Composio selection
friuns May 17, 2026
c4d3f82
Wait for Composio auth completion
friuns May 17, 2026
a32da0a
Optimize Composio directory loading
friuns May 18, 2026
2bc082e
Keep Composio cached reads warm
friuns May 18, 2026
90f757e
Refresh Composio connector row after connect
friuns May 18, 2026
56297e5
Refresh Composio suggestion before connect redirect
friuns May 18, 2026
bb4da78
Simplify Composio suggestion label
friuns May 18, 2026
434f6d4
Show only connector name in suggestions
friuns May 18, 2026
17361c9
Hide Composio suggestion after click
friuns May 18, 2026
d534460
Address Composio suggestion review comments
friuns May 18, 2026
7569d21
Match latest Composio connector mention
friuns May 21, 2026
c75934b
Clean up Composio suggestion refresh
friuns May 22, 2026
5fc671d
Dedupe Composio docs by connector
friuns May 22, 2026
5d6dfa2
Simplify Composio directory helpers
friuns May 26, 2026
cfaf89e
Simplify Composio suggestion scoring
friuns May 26, 2026
8fbac94
Simplify Composio integration structure
friuns May 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
1. commit task in feature worktree
2. create/switch feature branch
3. rebase feature branch on `main`
4. from main worktree: `git checkout main && git merge --no-ff <feature-branch>`
4. if a GitHub PR exists or GitHub merge is available, prefer updating/creating the PR, rebasing the branch, and merging the PR on GitHub instead of merging locally
5. if no PR/GitHub merge path is available, from main worktree: `git checkout main && git merge --no-ff <feature-branch>`
- If user explicitly asks for a single merge commit, use:
- `git checkout main`
- `git reset --hard <pre-merge-main-commit>`
Expand Down
28 changes: 26 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@
<span class="sidebar-settings-label">{{ t('Chat width') }}</span>
<span class="sidebar-settings-value">{{ chatWidthLabel }}</span>
</button>
<button class="sidebar-settings-row" type="button" :title="SETTINGS_HELP.composioSuggestions" @click="toggleComposioSuggestions">
<span class="sidebar-settings-label">{{ t('Connector suggestions') }}</span>
<span class="sidebar-settings-toggle" :class="{ 'is-on': composioSuggestionsEnabled }" />
</button>
<button class="sidebar-settings-row" type="button" :title="SETTINGS_HELP.dictationClickToToggle" @click="toggleDictationClickToToggle">
<span class="sidebar-settings-label">{{ t('Click to toggle dictation') }}</span>
<span class="sidebar-settings-toggle" :class="{ 'is-on': dictationClickToToggle }" />
Expand Down Expand Up @@ -938,11 +942,13 @@
:is-interrupting-turn="false" :send-with-enter="sendWithEnter" :in-progress-submit-mode="inProgressSendMode"
:dictation-click-to-toggle="dictationClickToToggle" :dictation-auto-send="dictationAutoSend"
:dictation-language="dictationLanguage"
:composio-suggestions-enabled="composioSuggestionsEnabled"
@submit="onSubmitThreadMessage"
@update:selected-collaboration-mode="onSelectCollaborationMode"
@update:selected-model="onSelectModel"
@update:selected-reasoning-effort="onSelectReasoningEffort"
@update:selected-speed-mode="onSelectSpeedMode" />
@update:selected-speed-mode="onSelectSpeedMode"
@open-composio-connector="onOpenComposioConnectorFromComposer" />
</div>
</div>
</template>
Expand Down Expand Up @@ -1023,10 +1029,12 @@
:send-with-enter="sendWithEnter" :in-progress-submit-mode="inProgressSendMode"
:dictation-click-to-toggle="dictationClickToToggle" :dictation-auto-send="dictationAutoSend"
:dictation-language="dictationLanguage"
:composio-suggestions-enabled="composioSuggestionsEnabled"
@update:selected-collaboration-mode="onSelectCollaborationMode"
@submit="onSubmitThreadMessage" @update:selected-model="onSelectModel"
@update:selected-reasoning-effort="onSelectReasoningEffort"
@update:selected-speed-mode="onSelectSpeedMode"
@open-composio-connector="onOpenComposioConnectorFromComposer"
@interrupt="onInterruptTurn" />
</div>
</template>
Expand Down Expand Up @@ -1189,6 +1197,7 @@ const SETTINGS_HELP = {
inProgressSendMode: t('If a turn is still running, choose whether a new prompt should steer the current turn or be queued.'),
appearance: t('Switch between system theme, light mode, and dark mode.'),
chatWidth: t('Choose how wide the conversation column and composer can grow on desktop screens.'),
composioSuggestions: t('Show connector suggestions in the composer while typing connector names.'),
dictationClickToToggle: t('Use click-to-start and click-to-stop dictation instead of hold-to-talk.'),
dictationAutoSend: t('Automatically send transcribed dictation when recording stops.'),
dictationLanguage: t('Choose transcription language or keep auto-detect.'),
Expand Down Expand Up @@ -1216,6 +1225,7 @@ type DirectoryTryItemPayload = {
skillPath?: string
prompt?: string
attachedSkills?: Array<{ name: string; path: string }>
fileAttachments?: Array<{ label: string; path: string; fsPath: string }>
}

type ChatWidthPreset = {
Expand Down Expand Up @@ -1540,6 +1550,7 @@ const DARK_MODE_KEY = 'codex-web-local.dark-mode.v1'
const DICTATION_CLICK_TO_TOGGLE_KEY = 'codex-web-local.dictation-click-to-toggle.v1'
const DICTATION_AUTO_SEND_KEY = 'codex-web-local.dictation-auto-send.v1'
const DICTATION_LANGUAGE_KEY = 'codex-web-local.dictation-language.v1'
const COMPOSIO_SUGGESTIONS_ENABLED_KEY = 'codex-web-local.composio-suggestions-enabled.v1'

const CHAT_WIDTH_KEY = 'codex-web-local.chat-width.v1'
const MOBILE_RESUME_RELOAD_MIN_HIDDEN_MS = 400
Expand All @@ -1550,6 +1561,7 @@ const chatWidth = ref<ChatWidthMode>(loadChatWidthPref())
const dictationClickToToggle = ref(loadBoolPref(DICTATION_CLICK_TO_TOGGLE_KEY, false))
const dictationAutoSend = ref(loadBoolPref(DICTATION_AUTO_SEND_KEY, true))
const dictationLanguage = ref(loadDictationLanguagePref())
const composioSuggestionsEnabled = ref(loadBoolPref(COMPOSIO_SUGGESTIONS_ENABLED_KEY, true))
const dictationLanguageOptions = computed(() => buildDictationLanguageOptions())
const showFirstLaunchPluginsCard = ref(false)
const freeModeEnabled = ref(false)
Expand Down Expand Up @@ -1753,6 +1765,12 @@ function onOpenPluginsHomeCard(): void {
void router.push({ name: 'skills', query: { tab: 'plugins' } })
}

function onOpenComposioConnectorFromComposer(slug: string): void {
const normalizedSlug = slug.trim()
if (!normalizedSlug) return
void router.push({ name: 'skills', query: { tab: 'composio', connector: normalizedSlug } })
}

const threadContextBadgeState = computed(() => {
const remainingPercent = selectedThreadTokenUsage.value?.remainingContextPercent
if (remainingPercent === null || typeof remainingPercent !== 'number') return 'pending'
Expand Down Expand Up @@ -4029,6 +4047,11 @@ function toggleDictationAutoSend(): void {
window.localStorage.setItem(DICTATION_AUTO_SEND_KEY, dictationAutoSend.value ? '1' : '0')
}

function toggleComposioSuggestions(): void {
composioSuggestionsEnabled.value = !composioSuggestionsEnabled.value
window.localStorage.setItem(COMPOSIO_SUGGESTIONS_ENABLED_KEY, composioSuggestionsEnabled.value ? '1' : '0')
}


async function onProviderChange(provider: string): Promise<void> {
if (freeModeLoading.value) return
Expand Down Expand Up @@ -4631,9 +4654,10 @@ async function onTryDirectoryItem(payload: DirectoryTryItemPayload): Promise<voi
: payload.kind === 'skill' && payload.skillPath
? [{ name: payload.name, path: payload.skillPath }]
: []
const fileAttachments = payload.fileAttachments?.length ? payload.fileAttachments : []
try {
const targetCwd = directoryCwd.value.trim() || composerCwd.value.trim()
const threadId = await sendMessageToNewThread(text, targetCwd, [], skills, [])
const threadId = await sendMessageToNewThread(text, targetCwd, [], skills, fileAttachments)
if (!threadId) return
await router.replace({ name: 'thread', params: { threadId } })
scheduleMobileConversationJumpToLatest()
Expand Down
32 changes: 31 additions & 1 deletion src/api/codexGateway.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { getAvailableModelIds, getThreadDetail, listDirectoryComposioConnectors, resumeThread, startThreadTurn } from './codexGateway'
import { getAvailableModelIds, getThreadDetail, listDirectoryComposioConnectors, logoutDirectoryComposioCli, resumeThread, startThreadTurn } from './codexGateway'

function mockRpcFetch(): { requests: Array<{ method: string, params: Record<string, unknown> }> } {
const requests: Array<{ method: string, params: Record<string, unknown> }> = []
Expand Down Expand Up @@ -88,6 +88,36 @@ describe('listDirectoryComposioConnectors', () => {
})
})

describe('logoutDirectoryComposioCli', () => {
afterEach(() => {
vi.unstubAllGlobals()
})

it('posts to the logout endpoint', async () => {
const requests: Array<{ input: string, method: string }> = []
vi.stubGlobal('fetch', vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
requests.push({
input: String(input),
method: String(init?.method ?? 'GET'),
})
return new Response(JSON.stringify({
ok: true,
command: 'composio logout',
output: '',
}), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
})
}))

await logoutDirectoryComposioCli()

expect(requests).toEqual([{ input: '/codex-api/composio/logout', method: 'POST' }])
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
})

describe('getAvailableModelIds', () => {
afterEach(() => {
vi.unstubAllGlobals()
Expand Down
26 changes: 22 additions & 4 deletions src/api/codexGateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ export type DirectoryComposioInstallResult = {
output: string
}

export type DirectoryComposioLogoutResult = {
ok: boolean
command: string
output: string
}

type DirectoryComposioConnectorPage = {
data: DirectoryComposioConnector[]
nextCursor: string | null
Expand Down Expand Up @@ -2306,8 +2312,8 @@ export async function startDirectoryMcpLogin(name: string): Promise<DirectoryMcp
}
}

export async function getDirectoryComposioStatus(): Promise<DirectoryComposioStatus> {
const response = await fetch('/codex-api/composio/status')
export async function getDirectoryComposioStatus(force = false): Promise<DirectoryComposioStatus> {
const response = await fetch(`/codex-api/composio/status${force ? '?force=1' : ''}`)
if (!response.ok) {
throw new Error(`Failed to load Composio status (${response.status})`)
}
Expand Down Expand Up @@ -2336,8 +2342,10 @@ export async function listDirectoryComposioConnectors(
}
}

export async function readDirectoryComposioConnector(slug: string): Promise<DirectoryComposioConnectorDetail> {
const response = await fetch(`/codex-api/composio/connector?slug=${encodeURIComponent(slug)}`)
export async function readDirectoryComposioConnector(slug: string, force = false): Promise<DirectoryComposioConnectorDetail> {
const params = new URLSearchParams({ slug })
if (force) params.set('force', '1')
const response = await fetch(`/codex-api/composio/connector?${params.toString()}`)
if (!response.ok) {
throw new Error(`Failed to load Composio connector (${response.status})`)
}
Expand Down Expand Up @@ -2376,6 +2384,16 @@ export async function installDirectoryComposioCli(): Promise<DirectoryComposioIn
return await response.json() as DirectoryComposioInstallResult
}

export async function logoutDirectoryComposioCli(): Promise<DirectoryComposioLogoutResult> {
const response = await fetch('/codex-api/composio/logout', {
method: 'POST',
})
if (!response.ok) {
throw new Error(`Failed to logout Composio CLI (${response.status})`)
}
return await response.json() as DirectoryComposioLogoutResult
}

export async function getAccountRateLimitsResponse(): Promise<GetAccountRateLimitsResponse> {
return await callRpc<GetAccountRateLimitsResponse>('account/rateLimits/read')
}
Expand Down
Loading