feat: add Grok Build support (#1030)

* test: define Grok Build integration behavior

* feat: add Grok Build agent integration

* test: cover Grok permissions and resume paths

* docs: add Grok Build setup guide

* fix: scope Grok ACP discovery to session cwd

* fix: align Grok permission UI semantics

* docs: clarify Grok runner setup

* test: require Grok create model and effort options

* feat: add Grok create model and effort pickers

* test: define Grok runtime parity behavior

* feat: add Grok runtime ACP controls and discovery

* fix: tighten Grok runtime controls

* fix: suppress nonfatal Grok title quota errors

* feat: support Grok Auto permission mode

* feat: forward ACP native session titles for Grok

* fix: guard Grok Windows shell arguments
This commit is contained in:
SSU-WEI HUANG
2026-07-13 08:41:30 +08:00
committed by GitHub
parent de07643828
commit b9eed7c071
92 changed files with 3796 additions and 63 deletions
+41 -1
View File
@@ -62,6 +62,8 @@ import {
} from '@/lib/sessionChatCursorModel'
import { buildCursorEffortPickerOptions, resolveCursorVariantOptions } from '@/lib/cursorModelOptions'
import { useOpencodeModels } from '@/hooks/queries/useOpencodeModels'
import { useGrokModels } from '@/hooks/queries/useGrokModels'
import { useGrokReasoningEffortOptions } from '@/hooks/queries/useGrokReasoningEffortOptions'
import { usePiModels } from '@/hooks/queries/usePiModels'
import { useOpencodeReasoningEffortOptions } from '@/hooks/queries/useOpencodeReasoningEffortOptions'
import { useVoiceOptional } from '@/lib/voice-context'
@@ -577,6 +579,27 @@ function SessionChatInner(props: SessionChatProps) {
label: opencodeModel.name ?? opencodeModel.modelId
}))
}, [agentFlavor, opencodeModelsState.availableModels])
const grokModelsState = useGrokModels({
api: props.api,
sessionId: props.session.id,
enabled: agentFlavor === 'grok' && props.session.active && !controlledByUser
})
const grokEffortState = useGrokReasoningEffortOptions({
api: props.api,
sessionId: props.session.id,
enabled: agentFlavor === 'grok' && props.session.active && !controlledByUser
})
const grokModelOptions = useMemo(() => (
agentFlavor === 'grok'
? [
{ value: null, label: 'Default' },
...grokModelsState.availableModels.map((model) => ({
value: model.modelId,
label: model.name ?? model.modelId
}))
]
: undefined
), [agentFlavor, grokModelsState.availableModels])
const cursorModelsState = useCursorModels({
api: props.api,
sessionId: props.session.id,
@@ -1296,6 +1319,8 @@ function SessionChatInner(props: SessionChatProps) {
)
: agentFlavor === 'opencode'
? opencodeModelOptions
: agentFlavor === 'grok'
? grokModelOptions
// Pi uses its own provider-qualified picker (piModels prop).
// Feeding piModelOptions here would make the generic Ctrl/Cmd+M
// cycler (getNextModelForFlavor) post a bare modelId string,
@@ -1313,6 +1338,11 @@ function SessionChatInner(props: SessionChatProps) {
? opencodeReasoningEffortState.options
: undefined
}
availableEffortOptions={
agentFlavor === 'grok' && grokEffortState.options.length > 0
? grokEffortState.options
: undefined
}
active={props.session.active}
allowSendWhenInactive
thinking={props.session.thinking}
@@ -1361,6 +1391,10 @@ function SessionChatInner(props: SessionChatProps) {
: undefined)
: agentFlavor === 'pi'
? (props.session.active && !piModelsState.error ? handleModelChange : undefined)
: agentFlavor === 'grok'
? (props.session.active && !controlledByUser && !grokModelsState.error
? handleModelChange
: undefined)
: handleModelChange
}
onModelEffortChange={
@@ -1380,7 +1414,13 @@ function SessionChatInner(props: SessionChatProps) {
? handleModelReasoningEffortChange
: undefined
}
onEffortChange={handleEffortChange}
onEffortChange={
agentFlavor === 'grok'
? (props.session.active && !controlledByUser && grokEffortState.options.length > 0
? handleEffortChange
: undefined)
: handleEffortChange
}
serviceTier={agentFlavor === 'codex' ? props.session.serviceTier : undefined}
onServiceTierChange={
agentFlavor === 'codex'