feat: add agent ID/name resolution and provider/model syntax support
This commit is contained in:
@@ -75,7 +75,7 @@ export function createCli(): Command {
|
||||
.argument('<prompt>', 'The task/prompt for the agent')
|
||||
.option('-d, --detach', 'Run in background (detached)')
|
||||
.option('--name <name>', 'Assign a name/title to the agent')
|
||||
.option('--provider <provider>', 'Agent provider: claude | codex | opencode', 'claude')
|
||||
.option('--provider <provider>', 'Agent provider, or provider/model (e.g. codex or codex/gpt-5.4)', '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)')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Command } from 'commander'
|
||||
import { connectToDaemon, getDaemonHost } from '../../utils/client.js'
|
||||
import { connectToDaemon, getDaemonHost, resolveAgentId } from '../../utils/client.js'
|
||||
import type { CommandOptions, SingleResult, OutputSchema, CommandError } from '../../output/index.js'
|
||||
|
||||
/** Result type for agent archive command */
|
||||
@@ -38,7 +38,7 @@ export async function runArchiveCommand(
|
||||
const error: CommandError = {
|
||||
code: 'MISSING_AGENT_ID',
|
||||
message: 'Agent ID is required',
|
||||
details: 'Usage: paseo agent archive <id>',
|
||||
details: 'Usage: paseo agent archive <id-or-name>',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@@ -57,8 +57,10 @@ export async function runArchiveCommand(
|
||||
}
|
||||
|
||||
try {
|
||||
const fetchResult = await client.fetchAgent(agentIdArg)
|
||||
if (!fetchResult) {
|
||||
const agentsPayload = await client.fetchAgents({ filter: { includeArchived: true } })
|
||||
const agents = agentsPayload.entries.map((entry) => entry.agent)
|
||||
const agentId = resolveAgentId(agentIdArg, agents)
|
||||
if (!agentId) {
|
||||
const error: CommandError = {
|
||||
code: 'AGENT_NOT_FOUND',
|
||||
message: `Agent not found: ${agentIdArg}`,
|
||||
@@ -66,8 +68,10 @@ export async function runArchiveCommand(
|
||||
}
|
||||
throw error
|
||||
}
|
||||
const agent = fetchResult.agent
|
||||
const agentId = agent.id
|
||||
const agent = agents.find((entry) => entry.id === agentId)
|
||||
if (!agent) {
|
||||
throw new Error(`Resolved agent missing from fetched agents: ${agentId}`)
|
||||
}
|
||||
|
||||
// Check if agent is already archived
|
||||
if (agent.archivedAt) {
|
||||
|
||||
@@ -39,7 +39,7 @@ export function createAgentCommand(): Command {
|
||||
.argument('<prompt>', 'The task/prompt for the agent')
|
||||
.option('-d, --detach', 'Run in background (detached)')
|
||||
.option('--name <name>', 'Assign a name/title to the agent')
|
||||
.option('--provider <provider>', 'Agent provider: claude | codex | opencode', 'claude')
|
||||
.option('--provider <provider>', 'Agent provider, or provider/model (e.g. codex or codex/gpt-5.4)', '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)')
|
||||
@@ -121,7 +121,7 @@ export function createAgentCommand(): Command {
|
||||
agent
|
||||
.command('archive')
|
||||
.description('Archive an agent (soft-delete)')
|
||||
.argument('<id>', 'Agent ID (or prefix)')
|
||||
.argument('<id>', 'Agent ID, prefix, or name')
|
||||
.option('--force', 'Force archive running agent (interrupts active run first)')
|
||||
).action(withOutput(runArchiveCommand))
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ interface AgentInspect {
|
||||
Model: string
|
||||
Thinking: string
|
||||
Status: string
|
||||
Archived: boolean
|
||||
ArchivedAt: string | null
|
||||
Mode: string
|
||||
Cwd: string
|
||||
CreatedAt: string
|
||||
@@ -127,6 +129,8 @@ function toInspectData(snapshot: AgentSnapshotPayload): AgentInspect {
|
||||
Model: resolveModel(snapshot) ?? '-',
|
||||
Thinking: snapshot.effectiveThinkingOptionId ?? 'auto',
|
||||
Status: snapshot.status,
|
||||
Archived: snapshot.archivedAt != null,
|
||||
ArchivedAt: snapshot.archivedAt ?? null,
|
||||
Mode: snapshot.currentModeId ?? 'default',
|
||||
Cwd: snapshot.cwd,
|
||||
CreatedAt: snapshot.createdAt,
|
||||
@@ -154,6 +158,8 @@ function toInspectRows(agent: AgentInspect): InspectRow[] {
|
||||
{ key: 'Model', value: agent.Model },
|
||||
{ key: 'Thinking', value: agent.Thinking },
|
||||
{ key: 'Status', value: agent.Status },
|
||||
{ key: 'Archived', value: String(agent.Archived) },
|
||||
{ key: 'ArchivedAt', value: agent.ArchivedAt ?? 'null' },
|
||||
{ key: 'Mode', value: agent.Mode },
|
||||
{ key: 'Cwd', value: shortenPath(agent.Cwd) },
|
||||
{ key: 'CreatedAt', value: agent.CreatedAt },
|
||||
|
||||
@@ -46,6 +46,11 @@ export interface AgentRunOptions extends CommandOptions {
|
||||
outputSchema?: string
|
||||
}
|
||||
|
||||
interface ResolvedProviderModel {
|
||||
provider: string
|
||||
model: string | undefined
|
||||
}
|
||||
|
||||
function toRunResult(
|
||||
agent: AgentSnapshotPayload,
|
||||
statusOverride?: AgentRunResult['status']
|
||||
@@ -165,6 +170,52 @@ function structuredRunSchema(output: Record<string, unknown>): OutputSchema<Agen
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveProviderAndModel(options: Pick<AgentRunOptions, 'provider' | 'model'>): ResolvedProviderModel {
|
||||
const providerInput = options.provider?.trim() || 'claude'
|
||||
const modelInput = options.model?.trim()
|
||||
|
||||
if (options.model !== undefined && !modelInput) {
|
||||
const error: CommandError = {
|
||||
code: 'INVALID_MODEL',
|
||||
message: '--model cannot be empty',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
const slashIndex = providerInput.indexOf('/')
|
||||
if (slashIndex === -1) {
|
||||
return {
|
||||
provider: providerInput,
|
||||
model: modelInput,
|
||||
}
|
||||
}
|
||||
|
||||
const provider = providerInput.slice(0, slashIndex).trim()
|
||||
const modelFromProvider = providerInput.slice(slashIndex + 1).trim()
|
||||
if (!provider || !modelFromProvider) {
|
||||
const error: CommandError = {
|
||||
code: 'INVALID_PROVIDER',
|
||||
message: 'Invalid --provider value',
|
||||
details: 'Use --provider <provider> or --provider <provider>/<model>',
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
if (modelInput && modelInput !== modelFromProvider) {
|
||||
const error: CommandError = {
|
||||
code: 'CONFLICTING_MODEL_OPTIONS',
|
||||
message: 'Conflicting model values provided',
|
||||
details: `--provider specifies model ${modelFromProvider}, but --model specifies ${modelInput}`,
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
return {
|
||||
provider,
|
||||
model: modelInput ?? modelFromProvider,
|
||||
}
|
||||
}
|
||||
|
||||
export async function runRunCommand(
|
||||
prompt: string,
|
||||
options: AgentRunOptions,
|
||||
@@ -203,6 +254,8 @@ export async function runRunCommand(
|
||||
throw error
|
||||
}
|
||||
|
||||
const resolvedProviderModel = resolveProviderAndModel(options)
|
||||
|
||||
let client
|
||||
try {
|
||||
client = await connectToDaemon({ host: options.host as string | undefined })
|
||||
@@ -289,11 +342,11 @@ export async function runRunCommand(
|
||||
const callStructuredTurn = async (structuredPrompt: string): Promise<string> => {
|
||||
if (!structuredAgent) {
|
||||
structuredAgent = await client.createAgent({
|
||||
provider: (options.provider as 'claude' | 'codex' | 'opencode') ?? 'claude',
|
||||
provider: resolvedProviderModel.provider as 'claude' | 'codex' | 'opencode',
|
||||
cwd,
|
||||
title: options.name,
|
||||
modeId: options.mode,
|
||||
model: options.model,
|
||||
model: resolvedProviderModel.model,
|
||||
thinkingOptionId,
|
||||
initialPrompt: structuredPrompt,
|
||||
images,
|
||||
@@ -387,11 +440,11 @@ export async function runRunCommand(
|
||||
|
||||
// Create the agent
|
||||
const agent = await client.createAgent({
|
||||
provider: (options.provider as 'claude' | 'codex' | 'opencode') ?? 'claude',
|
||||
provider: resolvedProviderModel.provider as 'claude' | 'codex' | 'opencode',
|
||||
cwd,
|
||||
title: options.name,
|
||||
modeId: options.mode,
|
||||
model: options.model,
|
||||
model: resolvedProviderModel.model,
|
||||
thinkingOptionId,
|
||||
initialPrompt: prompt,
|
||||
images,
|
||||
|
||||
Reference in New Issue
Block a user