feat(cli): add --output-schema flag for structured agent output
This commit is contained in:
@@ -169,6 +169,7 @@ export type CreateAgentRequestOptions = {
|
||||
provider?: AgentProvider;
|
||||
cwd?: string;
|
||||
initialPrompt?: string;
|
||||
outputSchema?: Record<string, unknown>;
|
||||
images?: CreateAgentRequestMessage["images"];
|
||||
git?: GitSetupOptions;
|
||||
worktreeName?: string;
|
||||
@@ -224,6 +225,7 @@ export type WaitForFinishResult = {
|
||||
status: "idle" | "error" | "permission" | "timeout";
|
||||
final: AgentSnapshotPayload | null;
|
||||
error: string | null;
|
||||
lastMessage: string | null;
|
||||
};
|
||||
|
||||
type Waiter<T> = {
|
||||
@@ -984,6 +986,7 @@ export class DaemonClient {
|
||||
requestId,
|
||||
config,
|
||||
...(options.initialPrompt ? { initialPrompt: options.initialPrompt } : {}),
|
||||
...(options.outputSchema ? { outputSchema: options.outputSchema } : {}),
|
||||
...(options.images && options.images.length > 0
|
||||
? { images: options.images }
|
||||
: {}),
|
||||
@@ -2260,6 +2263,7 @@ export class DaemonClient {
|
||||
status: payload.status,
|
||||
final: payload.final,
|
||||
error: payload.error,
|
||||
lastMessage: payload.lastMessage,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ import type {
|
||||
AgentPermissionResponse,
|
||||
AgentPromptContentBlock,
|
||||
AgentPromptInput,
|
||||
AgentRunOptions,
|
||||
McpServerConfig,
|
||||
AgentSessionConfig,
|
||||
AgentStreamEvent,
|
||||
@@ -683,7 +684,8 @@ export class Session {
|
||||
*/
|
||||
private startAgentStream(
|
||||
agentId: string,
|
||||
prompt: AgentPromptInput
|
||||
prompt: AgentPromptInput,
|
||||
runOptions?: AgentRunOptions
|
||||
): { ok: true } | { ok: false; error: string } {
|
||||
this.sessionLogger.info(
|
||||
{ agentId },
|
||||
@@ -692,7 +694,7 @@ export class Session {
|
||||
|
||||
let iterator: AsyncGenerator<AgentStreamEvent>;
|
||||
try {
|
||||
iterator = this.agentManager.streamAgent(agentId, prompt);
|
||||
iterator = this.agentManager.streamAgent(agentId, prompt, runOptions);
|
||||
} catch (error) {
|
||||
this.handleAgentRunError(agentId, error, "Failed to start agent run");
|
||||
const message =
|
||||
@@ -2036,7 +2038,8 @@ export class Session {
|
||||
agentId: string,
|
||||
text: string,
|
||||
messageId?: string,
|
||||
images?: Array<{ data: string; mimeType: string }>
|
||||
images?: Array<{ data: string; mimeType: string }>,
|
||||
runOptions?: AgentRunOptions
|
||||
): Promise<void> {
|
||||
this.sessionLogger.info(
|
||||
{ agentId, textPreview: text.substring(0, 50), imageCount: images?.length ?? 0 },
|
||||
@@ -2076,7 +2079,7 @@ export class Session {
|
||||
);
|
||||
}
|
||||
|
||||
this.startAgentStream(agentId, prompt);
|
||||
this.startAgentStream(agentId, prompt, runOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2124,7 +2127,7 @@ export class Session {
|
||||
private async handleCreateAgentRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "create_agent_request" }>
|
||||
): Promise<void> {
|
||||
const { config, worktreeName, requestId, initialPrompt, git, images, labels } = msg;
|
||||
const { config, worktreeName, requestId, initialPrompt, outputSchema, git, images, labels } = msg;
|
||||
this.sessionLogger.info(
|
||||
{ cwd: config.cwd, provider: config.provider, worktreeName },
|
||||
`Creating agent in ${config.cwd} (${config.provider})${
|
||||
@@ -2163,7 +2166,8 @@ export class Session {
|
||||
snapshot.id,
|
||||
trimmedPrompt,
|
||||
uuidv4(),
|
||||
images
|
||||
images,
|
||||
outputSchema ? { outputSchema } : undefined
|
||||
);
|
||||
} catch (promptError) {
|
||||
this.sessionLogger.error(
|
||||
@@ -4832,7 +4836,7 @@ export class Session {
|
||||
if (!resolved.ok) {
|
||||
this.emit({
|
||||
type: "wait_for_finish_response",
|
||||
payload: { requestId, status: "error", final: null, error: resolved.error },
|
||||
payload: { requestId, status: "error", final: null, error: resolved.error, lastMessage: null },
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -4849,6 +4853,7 @@ export class Session {
|
||||
status: "error",
|
||||
final: null,
|
||||
error: `Agent not found: ${agentId}`,
|
||||
lastMessage: null,
|
||||
},
|
||||
});
|
||||
return;
|
||||
@@ -4862,7 +4867,7 @@ export class Session {
|
||||
: "idle";
|
||||
this.emit({
|
||||
type: "wait_for_finish_response",
|
||||
payload: { requestId, status, final, error: null },
|
||||
payload: { requestId, status, final, error: null, lastMessage: null },
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -4892,7 +4897,7 @@ export class Session {
|
||||
|
||||
this.emit({
|
||||
type: "wait_for_finish_response",
|
||||
payload: { requestId, status, final, error: null },
|
||||
payload: { requestId, status, final, error: null, lastMessage: result.lastMessage },
|
||||
});
|
||||
} catch (error) {
|
||||
const isAbort =
|
||||
@@ -4910,6 +4915,7 @@ export class Session {
|
||||
status: "error",
|
||||
final,
|
||||
error: message,
|
||||
lastMessage: null,
|
||||
},
|
||||
});
|
||||
return;
|
||||
@@ -4921,7 +4927,7 @@ export class Session {
|
||||
}
|
||||
this.emit({
|
||||
type: "wait_for_finish_response",
|
||||
payload: { requestId, status: "timeout", final, error: null },
|
||||
payload: { requestId, status: "timeout", final, error: null, lastMessage: null },
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeoutHandle);
|
||||
|
||||
@@ -548,6 +548,7 @@ export const CreateAgentRequestMessageSchema = z.object({
|
||||
config: AgentSessionConfigSchema,
|
||||
worktreeName: z.string().optional(),
|
||||
initialPrompt: z.string().optional(),
|
||||
outputSchema: z.record(z.unknown()).optional(),
|
||||
images: z.array(z.object({
|
||||
data: z.string(), // base64 encoded image
|
||||
mimeType: z.string(), // e.g., "image/jpeg", "image/png"
|
||||
@@ -1360,6 +1361,7 @@ export const WaitForFinishResponseMessageSchema = z.object({
|
||||
status: z.enum(["idle", "error", "permission", "timeout"]),
|
||||
final: AgentSnapshotPayloadSchema.nullable(),
|
||||
error: z.string().nullable(),
|
||||
lastMessage: z.string().nullable(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user