Make workspace, agent, and schedule automation consistent (#2186)
* feat(workspaces): align agent and schedule automation Make workspace identity the shared placement boundary for MCP and CLI, while caller identity determines parentage. Keep heartbeats minimal and cron-based without changing legacy rolling interval semantics. * test(cli): expect canonical schedule cadence * fix(automation): preserve workspace and schedule compatibility * fix(automation): preserve compatibility edges * fix(workspaces): align MCP lifecycle resolution * fix(workspaces): preserve creation intent * fix(workspaces): preserve branch and schedule identity
This commit is contained in:
@@ -13,7 +13,7 @@ import { Text, View } from "react-native";
|
||||
import { Brain, Folder, GitBranch } from "lucide-react-native";
|
||||
import { StyleSheet } from "react-native-unistyles";
|
||||
import type { AgentProvider } from "@getpaseo/protocol/agent-types";
|
||||
import type { ScheduleSummary } from "@getpaseo/protocol/schedule/types";
|
||||
import type { ScheduleCadence, ScheduleSummary } from "@getpaseo/protocol/schedule/types";
|
||||
import { useStoreWithEqualityFn } from "zustand/traditional";
|
||||
import { AdaptiveModalSheet, type SheetHeader } from "@/components/adaptive-modal-sheet";
|
||||
import { ComboboxItem } from "@/components/ui/combobox";
|
||||
@@ -71,6 +71,15 @@ function parseMaxRuns(raw: string): number | null {
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
function requireCronCadence(
|
||||
cadence: Extract<ScheduleCadence, { type: "cron" }> | undefined,
|
||||
): Extract<ScheduleCadence, { type: "cron" }> {
|
||||
if (!cadence) {
|
||||
throw new Error("Choose a cron cadence before creating this schedule");
|
||||
}
|
||||
return cadence;
|
||||
}
|
||||
|
||||
function resolveCreateServerId(input: {
|
||||
mode: "create" | "edit";
|
||||
serverId: string | null | undefined;
|
||||
@@ -306,18 +315,15 @@ function OpenScheduleFormSheet({
|
||||
]);
|
||||
|
||||
const submitAgentTarget = useCallback(async (): Promise<boolean> => {
|
||||
if (!schedule) {
|
||||
if (!schedule || !state.submitCadence) {
|
||||
return false;
|
||||
}
|
||||
await updateSchedule({
|
||||
id: schedule.id,
|
||||
name: state.name.trim() || null,
|
||||
prompt: state.prompt.trim(),
|
||||
cadence: state.submitCadence,
|
||||
maxRuns: parseMaxRuns(state.maxRuns),
|
||||
});
|
||||
return true;
|
||||
}, [schedule, state.maxRuns, state.name, state.prompt, state.submitCadence, updateSchedule]);
|
||||
}, [schedule, state.submitCadence, updateSchedule]);
|
||||
|
||||
const submitNewAgent = useCallback(async (): Promise<boolean> => {
|
||||
const provider = state.selectedProvider;
|
||||
@@ -333,7 +339,7 @@ function OpenScheduleFormSheet({
|
||||
id: schedule.id,
|
||||
name: state.name.trim() || null,
|
||||
prompt: state.prompt.trim(),
|
||||
cadence: state.submitCadence,
|
||||
...(state.submitCadence ? { cadence: state.submitCadence } : {}),
|
||||
newAgentConfig: {
|
||||
provider,
|
||||
model: state.selectedModel || null,
|
||||
@@ -353,7 +359,7 @@ function OpenScheduleFormSheet({
|
||||
await createSchedule({
|
||||
prompt: state.prompt.trim(),
|
||||
name: state.name.trim() || undefined,
|
||||
cadence: state.submitCadence,
|
||||
cadence: requireCronCadence(state.submitCadence),
|
||||
target: {
|
||||
type: "new-agent",
|
||||
config: {
|
||||
@@ -394,10 +400,12 @@ function OpenScheduleFormSheet({
|
||||
void handleSubmit();
|
||||
}, [handleSubmit]);
|
||||
|
||||
const header = useMemo<SheetHeader>(
|
||||
() => ({ title: mode === "edit" ? "Edit schedule" : "New schedule" }),
|
||||
[mode],
|
||||
);
|
||||
const header = useMemo<SheetHeader>(() => {
|
||||
if (mode !== "edit") {
|
||||
return { title: "New schedule" };
|
||||
}
|
||||
return { title: schedule?.target.type === "agent" ? "Edit heartbeat" : "Edit schedule" };
|
||||
}, [mode, schedule?.target.type]);
|
||||
|
||||
const footer = useMemo(
|
||||
() => (
|
||||
@@ -466,6 +474,21 @@ function ScheduleFormFields({
|
||||
cadenceError,
|
||||
mutationServerId,
|
||||
}: ScheduleFormFieldsProps): ReactElement {
|
||||
if (state.targetKind === "agent") {
|
||||
return (
|
||||
<>
|
||||
<ScheduleAgentTargetField label={agentTargetLabel} size={controlSize} />
|
||||
<CadenceEditor
|
||||
value={state.cadence}
|
||||
onChange={model.setCadence}
|
||||
error={cadenceError ?? undefined}
|
||||
size={controlSize}
|
||||
/>
|
||||
{state.submitError ? <Text style={styles.submitError}>{state.submitError}</Text> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field label="Name">
|
||||
@@ -502,7 +525,7 @@ function ScheduleFormFields({
|
||||
model={model}
|
||||
state={state}
|
||||
providerSnapshot={providerSnapshot}
|
||||
agentTargetLabel={agentTargetLabel}
|
||||
agentTargetLabel={null}
|
||||
controlSize={controlSize}
|
||||
mutationServerId={mutationServerId}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user