feat: add thinking option support and autonomous run handling
This commit is contained in:
@@ -27,6 +27,7 @@ export function createAgentCommand(): Command {
|
||||
.option('-a, --all', 'Include archived agents')
|
||||
.option('-g, --global', 'Legacy no-op (kept for compatibility)')
|
||||
.option('--label <key=value>', 'Filter by label (can be used multiple times)', collectMultiple, [])
|
||||
.option('--thinking <id>', 'Filter by thinking option ID')
|
||||
.option('--json', 'Output in JSON format')
|
||||
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
|
||||
.action(withOutput(runLsCommand))
|
||||
@@ -39,6 +40,7 @@ export function createAgentCommand(): Command {
|
||||
.option('--name <name>', 'Assign a name/title to the agent')
|
||||
.option('--provider <provider>', 'Agent provider: claude | codex | opencode', 'claude')
|
||||
.option('--model <model>', 'Model to use (e.g., claude-sonnet-4-20250514, claude-3-5-haiku-20241022)')
|
||||
.option('--thinking <id>', 'Thinking option ID to use for this run')
|
||||
.option('--mode <mode>', 'Provider-specific mode (e.g., plan, default, bypass)')
|
||||
.option('--cwd <path>', 'Working directory (default: current)')
|
||||
.option('--label <key=value>', 'Add label(s) to the agent (can be used multiple times)', collectMultiple, [])
|
||||
|
||||
@@ -9,6 +9,7 @@ interface AgentInspect {
|
||||
Name: string
|
||||
Provider: string
|
||||
Model: string
|
||||
Thinking: string
|
||||
Status: string
|
||||
Mode: string
|
||||
Cwd: string
|
||||
@@ -84,6 +85,17 @@ function formatCost(costUsd: number): string {
|
||||
return `$${costUsd.toFixed(2)}`
|
||||
}
|
||||
|
||||
function normalizeModelId(value: string | null | undefined): string | null {
|
||||
if (typeof value !== 'string') return null
|
||||
const normalized = value.trim()
|
||||
if (!normalized || normalized.toLowerCase() === 'default') return null
|
||||
return normalized
|
||||
}
|
||||
|
||||
function resolveModel(snapshot: AgentSnapshotPayload): string | null {
|
||||
return normalizeModelId(snapshot.runtimeInfo?.model) ?? normalizeModelId(snapshot.model)
|
||||
}
|
||||
|
||||
/** Convert agent snapshot to inspection data */
|
||||
function toInspectData(snapshot: AgentSnapshotPayload): AgentInspect {
|
||||
const lastUsage = snapshot.lastUsage
|
||||
@@ -112,7 +124,8 @@ function toInspectData(snapshot: AgentSnapshotPayload): AgentInspect {
|
||||
Id: snapshot.id,
|
||||
Name: snapshot.title ?? '-',
|
||||
Provider: snapshot.provider,
|
||||
Model: snapshot.model ?? '-',
|
||||
Model: resolveModel(snapshot) ?? '-',
|
||||
Thinking: snapshot.effectiveThinkingOptionId ?? 'auto',
|
||||
Status: snapshot.status,
|
||||
Mode: snapshot.currentModeId ?? 'default',
|
||||
Cwd: snapshot.cwd,
|
||||
@@ -139,6 +152,7 @@ function toInspectRows(agent: AgentInspect): InspectRow[] {
|
||||
{ key: 'Name', value: agent.Name },
|
||||
{ key: 'Provider', value: agent.Provider },
|
||||
{ key: 'Model', value: agent.Model },
|
||||
{ key: 'Thinking', value: agent.Thinking },
|
||||
{ key: 'Status', value: agent.Status },
|
||||
{ key: 'Mode', value: agent.Mode },
|
||||
{ key: 'Cwd', value: shortenPath(agent.Cwd) },
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface AgentListItem {
|
||||
shortId: string
|
||||
name: string
|
||||
provider: string
|
||||
thinking: string
|
||||
status: string
|
||||
cwd: string
|
||||
created: string
|
||||
@@ -35,6 +36,13 @@ function shortenPath(path: string): string {
|
||||
return path
|
||||
}
|
||||
|
||||
function normalizeModelId(modelId: string | null | undefined): string | null {
|
||||
if (typeof modelId !== 'string') return null
|
||||
const normalized = modelId.trim()
|
||||
if (!normalized || normalized.toLowerCase() === 'default') return null
|
||||
return normalized
|
||||
}
|
||||
|
||||
/** Schema for agent ls output */
|
||||
export const agentLsSchema: OutputSchema<AgentListItem> = {
|
||||
idField: 'shortId',
|
||||
@@ -42,6 +50,7 @@ export const agentLsSchema: OutputSchema<AgentListItem> = {
|
||||
{ header: 'AGENT ID', field: 'shortId', width: 12 },
|
||||
{ header: 'NAME', field: 'name', width: 20 },
|
||||
{ header: 'PROVIDER', field: 'provider', width: 15 },
|
||||
{ header: 'THINKING', field: 'thinking', width: 12 },
|
||||
{
|
||||
header: 'STATUS',
|
||||
field: 'status',
|
||||
@@ -60,11 +69,13 @@ export const agentLsSchema: OutputSchema<AgentListItem> = {
|
||||
|
||||
/** Transform agent snapshot to AgentListItem */
|
||||
function toListItem(agent: AgentSnapshotPayload): AgentListItem {
|
||||
const model = normalizeModelId(agent.runtimeInfo?.model) ?? normalizeModelId(agent.model)
|
||||
return {
|
||||
id: agent.id,
|
||||
shortId: agent.id.slice(0, 7),
|
||||
name: agent.title ?? '-',
|
||||
provider: agent.model ? `${agent.provider}/${agent.model}` : agent.provider,
|
||||
provider: model ? `${agent.provider}/${model}` : agent.provider,
|
||||
thinking: agent.effectiveThinkingOptionId ?? 'auto',
|
||||
status: agent.status,
|
||||
cwd: shortenPath(agent.cwd),
|
||||
created: relativeTime(agent.createdAt),
|
||||
@@ -84,6 +95,8 @@ export interface AgentLsOptions extends CommandOptions {
|
||||
cwd?: string
|
||||
/** Filter by labels (key=value format) */
|
||||
label?: string[]
|
||||
/** Filter by thinking option ID */
|
||||
thinking?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,8 +124,45 @@ export async function runLsCommand(
|
||||
}
|
||||
|
||||
try {
|
||||
const normalizedThinkingOptionId = options.thinking?.trim()
|
||||
if (options.thinking !== undefined && !normalizedThinkingOptionId) {
|
||||
const error: CommandError = {
|
||||
code: 'INVALID_THINKING_OPTION',
|
||||
message: '--thinking cannot be empty',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
// Parse --label filters (key=value).
|
||||
const labelFilters: Record<string, string> = {}
|
||||
if (options.label) {
|
||||
for (const labelStr of options.label) {
|
||||
const eqIndex = labelStr.indexOf('=')
|
||||
if (eqIndex !== -1) {
|
||||
const key = labelStr.slice(0, eqIndex)
|
||||
const value = labelStr.slice(eqIndex + 1)
|
||||
labelFilters[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const daemonFilter: {
|
||||
includeArchived?: boolean
|
||||
labels?: Record<string, string>
|
||||
thinkingOptionId?: string
|
||||
} = {}
|
||||
if (options.all) {
|
||||
daemonFilter.includeArchived = true
|
||||
}
|
||||
if (Object.keys(labelFilters).length > 0) {
|
||||
daemonFilter.labels = labelFilters
|
||||
}
|
||||
if (normalizedThinkingOptionId) {
|
||||
daemonFilter.thinkingOptionId = normalizedThinkingOptionId
|
||||
}
|
||||
|
||||
const fetchPayload = await client.fetchAgents({
|
||||
filter: options.all ? { includeArchived: true } : undefined,
|
||||
filter: Object.keys(daemonFilter).length > 0 ? daemonFilter : undefined,
|
||||
})
|
||||
let agents = fetchPayload.entries.map((entry) => entry.agent)
|
||||
|
||||
@@ -135,19 +185,6 @@ export async function runLsCommand(
|
||||
})
|
||||
}
|
||||
|
||||
// Parse --label filters (key=value).
|
||||
const labelFilters: Record<string, string> = {}
|
||||
if (options.label) {
|
||||
for (const labelStr of options.label) {
|
||||
const eqIndex = labelStr.indexOf('=')
|
||||
if (eqIndex !== -1) {
|
||||
const key = labelStr.slice(0, eqIndex)
|
||||
const value = labelStr.slice(eqIndex + 1)
|
||||
labelFilters[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply label filtering only when explicitly requested.
|
||||
if (Object.keys(labelFilters).length > 0) {
|
||||
agents = agents.filter((a) => {
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface AgentRunOptions extends CommandOptions {
|
||||
name?: string
|
||||
provider?: string
|
||||
model?: string
|
||||
thinking?: string
|
||||
mode?: string
|
||||
worktree?: string
|
||||
base?: string
|
||||
@@ -219,6 +220,16 @@ export async function runRunCommand(
|
||||
try {
|
||||
// Resolve working directory
|
||||
const cwd = options.cwd ?? process.cwd()
|
||||
const thinkingOptionId = options.thinking?.trim()
|
||||
if (options.thinking !== undefined && !thinkingOptionId) {
|
||||
const error: CommandError = {
|
||||
code: 'INVALID_THINKING_OPTION',
|
||||
message: '--thinking cannot be empty',
|
||||
details:
|
||||
'Provide a thinking option ID. Use "paseo provider models <provider> --thinking" to list valid IDs.',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
// Process images if provided
|
||||
let images: Array<{ data: string; mimeType: string }> | undefined
|
||||
@@ -290,6 +301,7 @@ export async function runRunCommand(
|
||||
title: options.name,
|
||||
modeId: options.mode,
|
||||
model: options.model,
|
||||
thinkingOptionId,
|
||||
initialPrompt: structuredPrompt,
|
||||
images,
|
||||
git,
|
||||
@@ -387,6 +399,7 @@ export async function runRunCommand(
|
||||
title: options.name,
|
||||
modeId: options.mode,
|
||||
model: options.model,
|
||||
thinkingOptionId,
|
||||
initialPrompt: prompt,
|
||||
images,
|
||||
git,
|
||||
|
||||
Reference in New Issue
Block a user