diff --git a/src/commands/session.tsx b/src/commands/session.tsx index 5f2f4d9..7ab9b7c 100644 --- a/src/commands/session.tsx +++ b/src/commands/session.tsx @@ -205,6 +205,13 @@ export function registerSession(program: Command): void { if (opts.config) req.config_id = opts.config if (opts.configFile) req.config = readConfigFile(opts.configFile) if (opts.template) req.template_id = opts.template + // The repo we're standing in (origin remote), sent unconditionally — + // with no config source it picks the repo rung of the server's + // defaults ladder, and either way the server merges it into the + // sandbox checkout set. Outside a git repo (or with no usable + // remote) nothing is sent. + const contextRepo = repoFromCwd(process.cwd()) + if (contextRepo) req.repository = contextRepo // Sugar flags (--model, --repo, --cpu, ...) and the raw // --config-override are merged into one structured override, applied // onto the chosen (or default) config and re-validated server-side. @@ -217,7 +224,30 @@ export function registerSession(program: Command): void { const api = new ApiClient() const session = await api.startAgentSession(req) + // Say which agent the server picked when it came from the defaults + // ladder, so a bare `agent` never silently runs an unexpected config. + if (!opts.json && session.resolved_config_name) { + if (session.resolution_source === 'repo_default') { + console.log(`using config "${session.resolved_config_name}" (repo default)`) + } else if (session.resolution_source === 'account_default') { + console.log(`using config "${session.resolved_config_name}" (account default)`) + } + } + if (opts.connect) { + // A non-interactive config refuses the stream/messages surface, so + // a connect would fail; degrade to watching the output instead. + const ellipsisBlock = (session.agent_config as Record | undefined) + ?.ellipsis as { interactive?: boolean } | undefined + if (ellipsisBlock?.interactive === false) { + console.log( + 'this agent is not interactive; watching output instead of connecting', + ) + console.log(`✓ started session ${session.id}`) + await printSessionUrl(api, session.id) + await watchSessionStreaming(api, session.id, FALLBACK_POLL_INTERVAL_SECONDS, false) + return + } await startConnect(session) return } diff --git a/src/lib/types.ts b/src/lib/types.ts index 7b95624..9d33198 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -126,6 +126,11 @@ export interface AgentSession { cost_fee: number tokens_total: number metadata: Record + // Present on the POST /v1/sessions response only (StartAgentSessionResponse): + // which config the session runs under and which rung of the defaults ladder + // chose it (null when an explicit config/template bypassed resolution). + resolved_config_name?: string | null + resolution_source?: 'repo_default' | 'account_default' | 'none' | null [key: string]: unknown } @@ -172,6 +177,13 @@ export interface StartAgentSessionRequest { config?: AgentConfig template_id?: string handoff?: HandoffAgentSessionParams + // The "owner/name" repository the CLI is standing in (origin remote). With + // no explicit config source it selects the repo rung of the server's + // default-config ladder (repo default -> account default -> bare config), + // and it is always merged into the sandbox repository set (cloned at the + // default branch), even alongside --config. Unknown/foreign repos are + // ignored server-side. + repository?: string // No `source`: the server derives a session's provenance from the credential // (a user token => `cli`), so it can't be spoofed by the request body. metadata?: Record