The autopilot's start line counts its steps and folds open to the latest checkpoints, and its result reads as a card. The composer's plus menu starts an autopilot with the draft as the order and a choice of until done, tomorrow morning or a time. Today lists the autopilots that run, wait or ended within a day, and their step runs no longer show one by one. Autopilot runs send no completion or failure push; only a pause notifies. Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1 Co-authored-by: Claude <noreply@anthropic.com>
81 lines
3.0 KiB
TypeScript
81 lines
3.0 KiB
TypeScript
import * as z from "zod";
|
|
import { Id } from "./ids.js";
|
|
|
|
/** Continuations without a checkpoint, or failed runs in a row, before the autopilot pauses. */
|
|
export const AUTOPILOT_IDLE_LIMIT = 3;
|
|
export const AUTOPILOT_FAIL_LIMIT = 3;
|
|
|
|
export const AUTOPILOT_LIMITS = { goal: 500, focus: 200, checkpoint: 300, result: 2000 } as const;
|
|
/** Checkpoints kept on the start line; older ones only count toward the step total. */
|
|
export const AUTOPILOT_CHECKPOINT_LOG = 50;
|
|
|
|
export const AutopilotStatusSchema = z.enum(["active", "paused", "finished", "stopped"]);
|
|
export type AutopilotStatus = z.infer<typeof AutopilotStatusSchema>;
|
|
|
|
/**
|
|
* A quiet timeline line for an autopilot: when it started (with its goal, end time, focus and
|
|
* the approvals granted only for it), paused, stopped or finished. `ended` is set on the start
|
|
* line once the autopilot is over, so its Stop control goes away.
|
|
*/
|
|
export const AutopilotBlockSchema = z.object({
|
|
kind: z.literal("autopilot"),
|
|
event: z.enum([
|
|
"autopilot.started",
|
|
"autopilot.paused",
|
|
"autopilot.stopped",
|
|
"autopilot.finished",
|
|
]),
|
|
autopilotId: Id,
|
|
goal: z.string(),
|
|
focus: z.string().optional(),
|
|
until: z.string().optional(),
|
|
/** Tools or apps the bot may use without asking while this autopilot runs. */
|
|
allowed: z.array(z.string()).optional(),
|
|
steps: z.number().int().nonnegative().optional(),
|
|
/** The latest checkpoints, newest last, on the start line. */
|
|
checkpoints: z
|
|
.array(z.object({ text: z.string(), at: z.string() }))
|
|
.max(AUTOPILOT_CHECKPOINT_LOG)
|
|
.optional(),
|
|
durationMs: z.number().int().nonnegative().optional(),
|
|
/** Why it paused, or what it achieved. */
|
|
text: z.string().optional(),
|
|
ended: z.boolean().optional(),
|
|
});
|
|
export type AutopilotBlock = z.infer<typeof AutopilotBlockSchema>;
|
|
|
|
export const AutopilotSchema = z.object({
|
|
id: Id,
|
|
botId: Id,
|
|
threadId: Id,
|
|
goal: z.string(),
|
|
focus: z.string().nullable(),
|
|
until: z.string().nullable(),
|
|
status: AutopilotStatusSchema,
|
|
pauseReason: z.string().nullable(),
|
|
lastCheckpoint: z.string().nullable(),
|
|
nextStep: z.string().nullable(),
|
|
result: z.string().nullable(),
|
|
stepCount: z.number().int(),
|
|
startedAt: z.string(),
|
|
endedAt: z.string().nullable(),
|
|
});
|
|
export type Autopilot = z.infer<typeof AutopilotSchema>;
|
|
|
|
/** How long a finished or stopped autopilot stays in Today, so the night's work is there at breakfast. */
|
|
export const AUTOPILOT_RECENT_MS = 24 * 60 * 60 * 1000;
|
|
|
|
/** An autopilot in Today: open ones, and those that ended within AUTOPILOT_RECENT_MS. */
|
|
export const AutopilotRecentSchema = AutopilotSchema.extend({
|
|
spaceId: Id,
|
|
groupId: Id.nullable(),
|
|
});
|
|
export type AutopilotRecent = z.infer<typeof AutopilotRecentSchema>;
|
|
|
|
export const AutopilotStartInputSchema = z.object({
|
|
goal: z.string().trim().min(1).max(AUTOPILOT_LIMITS.goal),
|
|
focus: z.string().trim().max(AUTOPILOT_LIMITS.focus).optional(),
|
|
/** ISO time to stop at; without it the autopilot runs until the goal is done. */
|
|
until: z.string().datetime({ offset: true }).optional(),
|
|
});
|