@@ -14,11 +14,16 @@ import {
1414} from "@agentclientprotocol/sdk" ;
1515import { isMcpToolReadOnly } from "@posthog/agent" ;
1616import { hydrateSessionJsonl } from "@posthog/agent/adapters/claude/session/jsonl-hydration" ;
17+ import { getEffortOptions } from "@posthog/agent/adapters/claude/session/models" ;
1718import { Agent } from "@posthog/agent/agent" ;
19+ import { getAvailableModes } from "@posthog/agent/execution-mode" ;
1820import {
21+ DEFAULT_GATEWAY_MODEL ,
1922 fetchGatewayModels ,
2023 formatGatewayModelName ,
2124 getProviderName ,
25+ isAnthropicModel ,
26+ isOpenAIModel ,
2227} from "@posthog/agent/gateway-models" ;
2328import { getLlmGatewayUrl } from "@posthog/agent/posthog-api" ;
2429import type { OnLogCallback } from "@posthog/agent/types" ;
@@ -189,6 +194,8 @@ interface SessionConfig {
189194 customInstructions ?: string ;
190195 /** Effort level for Claude sessions */
191196 effort ?: EffortLevel ;
197+ /** Model to use for the session (e.g. "claude-sonnet-4-6") */
198+ model ?: string ;
192199}
193200
194201interface ManagedSession {
@@ -465,9 +472,10 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
465472 permissionMode,
466473 customInstructions,
467474 effort,
475+ model,
468476 } = config ;
469477
470- // Preview sessions don 't need a real repo — use a temp directory
478+ // Preview config doesn 't need a real repo — use a temp directory
471479 const repoPath = taskId === "__preview__" ? tmpdir ( ) : rawRepoPath ;
472480
473481 if ( ! isRetry ) {
@@ -638,6 +646,7 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
638646 sessionId : existingSessionId ,
639647 systemPrompt,
640648 ...( permissionMode && { permissionMode } ) ,
649+ ...( model != null && { model } ) ,
641650 claudeCode : {
642651 options : {
643652 ...( additionalDirectories ?. length && {
@@ -669,6 +678,7 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
669678 taskRunId,
670679 systemPrompt,
671680 ...( permissionMode && { permissionMode } ) ,
681+ ...( model != null && { model } ) ,
672682 claudeCode : {
673683 options : {
674684 ...( additionalDirectories ?. length && { additionalDirectories } ) ,
@@ -1362,6 +1372,7 @@ For git operations while detached:
13621372 customInstructions :
13631373 "customInstructions" in params ? params . customInstructions : undefined ,
13641374 effort : "effort" in params ? params . effort : undefined ,
1375+ model : "model" in params ? params . model : undefined ,
13651376 } ;
13661377 }
13671378
@@ -1513,4 +1524,98 @@ For git operations while detached:
15131524 return getModelTier ( a . modelId ) - getModelTier ( b . modelId ) ;
15141525 } ) ;
15151526 }
1527+
1528+ async getPreviewConfigOptions (
1529+ apiHost : string ,
1530+ adapter : "claude" | "codex" = "claude" ,
1531+ ) : Promise < SessionConfigOption [ ] > {
1532+ const gatewayUrl = getLlmGatewayUrl ( apiHost ) ;
1533+ const gatewayModels = await fetchGatewayModels ( { gatewayUrl } ) ;
1534+
1535+ const modelFilter = adapter === "codex" ? isOpenAIModel : isAnthropicModel ;
1536+
1537+ const modelOptions = gatewayModels
1538+ . filter ( ( model ) => modelFilter ( model ) )
1539+ . map ( ( model ) => ( {
1540+ value : model . id ,
1541+ name : formatGatewayModelName ( model ) ,
1542+ description : `Context: ${ model . context_window . toLocaleString ( ) } tokens` ,
1543+ } ) ) ;
1544+
1545+ const defaultModel =
1546+ adapter === "codex"
1547+ ? ( modelOptions [ 0 ] ?. value ?? "" )
1548+ : DEFAULT_GATEWAY_MODEL ;
1549+
1550+ const resolvedModelId = modelOptions . some ( ( o ) => o . value === defaultModel )
1551+ ? defaultModel
1552+ : ( modelOptions [ 0 ] ?. value ?? defaultModel ) ;
1553+
1554+ if ( ! modelOptions . some ( ( o ) => o . value === resolvedModelId ) ) {
1555+ modelOptions . unshift ( {
1556+ value : resolvedModelId ,
1557+ name : resolvedModelId ,
1558+ description : "Custom model" ,
1559+ } ) ;
1560+ }
1561+
1562+ const modeOptions = getAvailableModes ( ) . map ( ( mode ) => ( {
1563+ value : mode . id ,
1564+ name : mode . name ,
1565+ description : mode . description ?? undefined ,
1566+ } ) ) ;
1567+
1568+ const configOptions : SessionConfigOption [ ] = [
1569+ {
1570+ id : "mode" ,
1571+ name : "Approval Preset" ,
1572+ type : "select" ,
1573+ currentValue : "plan" ,
1574+ options : modeOptions ,
1575+ category : "mode" ,
1576+ description :
1577+ "Choose an approval and sandboxing preset for your session" ,
1578+ } ,
1579+ {
1580+ id : "model" ,
1581+ name : "Model" ,
1582+ type : "select" ,
1583+ currentValue : resolvedModelId ,
1584+ options : modelOptions ,
1585+ category : "model" ,
1586+ description : "Choose which model Claude should use" ,
1587+ } ,
1588+ ] ;
1589+
1590+ if ( adapter === "codex" ) {
1591+ configOptions . push ( {
1592+ id : "reasoning_effort" ,
1593+ name : "Reasoning Level" ,
1594+ type : "select" ,
1595+ currentValue : "high" ,
1596+ options : [
1597+ { value : "low" , name : "Low" } ,
1598+ { value : "medium" , name : "Medium" } ,
1599+ { value : "high" , name : "High" } ,
1600+ ] ,
1601+ category : "thought_level" ,
1602+ description : "Controls how much reasoning effort the model uses" ,
1603+ } ) ;
1604+ } else {
1605+ const effortOpts = getEffortOptions ( resolvedModelId ) ;
1606+ if ( effortOpts ) {
1607+ configOptions . push ( {
1608+ id : "effort" ,
1609+ name : "Effort" ,
1610+ type : "select" ,
1611+ currentValue : "high" ,
1612+ options : effortOpts ,
1613+ category : "thought_level" ,
1614+ description : "Controls how much effort Claude puts into its response" ,
1615+ } ) ;
1616+ }
1617+ }
1618+
1619+ return configOptions ;
1620+ }
15161621}
0 commit comments